package cn.iocoder.yudao.module.system.service.ai;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.system.controller.admin.ai.vo.*;
import cn.iocoder.yudao.module.system.dal.dataobject.college.CollegeDO;
import cn.iocoder.yudao.module.system.dal.dataobject.collegeenrollmentplan.CollegeEnrollmentPlanDO;
import cn.iocoder.yudao.module.system.dal.dataobject.gugu.CollegeScoreDO;
import cn.iocoder.yudao.module.system.dal.dataobject.gugu.MajorAdmissionDO;
import cn.iocoder.yudao.module.system.dal.dataobject.ai.AiAdmissionCutoffScoreDO;
import cn.iocoder.yudao.module.system.dal.mysql.ai.AiAdmissionCutoffScoreMapper;
import cn.iocoder.yudao.module.system.dal.mysql.college.CollegeMapper;
import cn.iocoder.yudao.module.system.dal.mysql.collegeenrollmentplan.CollegeEnrollmentPlanMapper;
import cn.iocoder.yudao.module.system.dal.mysql.gugu.CollegeScoreMapper;
import cn.iocoder.yudao.module.system.dal.mysql.gugu.MajorAdmissionMapper;
import cn.iocoder.yudao.module.system.util.SchoolNameUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * AI智能选志愿服务实现
 *
 * @author 芋道源码
 */
@Service
@Slf4j
public class AiVolunteerRecommendServiceImpl implements AiVolunteerRecommendService {

    /**
     * 并行查询线程池
     */
    private final ExecutorService executorService = Executors.newFixedThreadPool(16);

    private final ExecutorService ioExecutor = new ThreadPoolExecutor(8, 32, 60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(1000),
            r -> new Thread(r, "ai-io-" + System.currentTimeMillis()),
            new ThreadPoolExecutor.CallerRunsPolicy());

    @Resource
    private CollegeEnrollmentPlanMapper enrollmentPlanMapper;

    @Resource
    private MajorAdmissionMapper majorAdmissionMapper;

    @Resource
    private CollegeScoreMapper collegeScoreMapper;

    @Resource
    private CollegeMapper collegeMapper;

    @Resource
    private AiAdmissionCutoffScoreMapper aiAdmissionCutoffScoreMapper;

    /**
     * 【第二阶段优化】Redis缓存服务
     * 用于缓存学校信息、历史录取数据等，减少数据库查询
     */
    @Resource
    private AiVolunteerRecommendCacheService cacheService;

    @Override
    public AiVolunteerRecommendRespVO recommendVolunteers(AiVolunteerRecommendReqVO reqVO) {
        long startTime = System.currentTimeMillis();

        // 判断是否启用提前分页优化（只有设置了分数区间才启用）
        boolean enableEarlyPagination = reqVO.getMinScore() != null 
                && reqVO.getMaxScore() != null 
                && StrUtil.isBlank(reqVO.getUniversityName());
        
        if (enableEarlyPagination) {
            return recommendVolunteersWithEarlyPagination(reqVO, startTime);
        } else {
            return recommendVolunteersNormal(reqVO, startTime);
        }
    }

    /**
     * 【提前分页优化】推荐志愿（新流程）
     * 先从AI录取分数线表获取排序好的专业组（带分页），再查询详细数据
     */
    private AiVolunteerRecommendRespVO recommendVolunteersWithEarlyPagination(
            AiVolunteerRecommendReqVO reqVO, long startTime) {
        
        // ========== 第1步：提前分页 - 从AI录取分数线表获取排序好的专业组 ==========
        long step1Start = System.currentTimeMillis();
        
        // 1.1 根据 type 参数调整分数区间（精准定位目标数据，避免查到无用的高分/低分数据）
        Integer adjustedMinScore = reqVO.getMinScore();
        Integer adjustedMaxScore = reqVO.getMaxScore();
        
        if (StrUtil.isNotBlank(reqVO.getType()) && reqVO.getScore() != null) {
            switch (reqVO.getType()) {
                case "CHONG": // 冲刺：录取分数 > 用户分数
                    adjustedMinScore = reqVO.getScore() + 1;
                    break;
                case "WEN": // 稳妥：录取分数 in [用户分数-5, 用户分数]
                    adjustedMinScore = Math.max(reqVO.getScore() - 5, reqVO.getMinScore() != null ? reqVO.getMinScore() : 0);
                    adjustedMaxScore = Math.min(reqVO.getScore(), reqVO.getMaxScore() != null ? reqVO.getMaxScore() : 999);
                    break;
                case "BAO": // 保底：录取分数 < 用户分数-5
                    adjustedMaxScore = reqVO.getScore() - 6;
                    break;
            }
        }
        
        // 1.2 查询所有符合条件的专业组（不限制数量，确保 total 准确）
        // 注意：这里不能只查 pageSize * 2，否则 total 会不准确
        // 提前分页的优化点在于：通过分数区间缩小数据范围，而不是限制查询数量  
        List<AiAdmissionCutoffScoreDO> cutoffScores = 
            aiAdmissionCutoffScoreMapper.selectMajorGroupsWithPagination(
                reqVO.getProvince(),
                reqVO.getYear(),
                reqVO.getClassify(),
                reqVO.getBatch(),
                adjustedMinScore,
                adjustedMaxScore,
                0,  // 不跳过任何数据
                Integer.MAX_VALUE  // 查询所有数据
            );
        
        log.info("步骤1-提前分页查询：获取{}个专业组，耗时：{}ms", 
                cutoffScores.size(), System.currentTimeMillis() - step1Start);
        
        // 1.3 选科要求过滤（应用于AI录取分数线数据）
        if (StrUtil.isNotBlank(reqVO.getSubjects())) {
            List<String> userSubjects = Arrays.asList(reqVO.getSubjects().split(","));
            cutoffScores = cutoffScores.stream()
                .filter(cutoff -> matchSubjectRequirementsForCount(cutoff.getSubjectRequirement(), userSubjects))
                .collect(Collectors.toList());
        }
        
        if (cutoffScores.isEmpty()) {
            return buildEmptyResponse(reqVO);
        }
        
        // ========== 第2步：提取符合条件的学校UUID列表，查询所有招生计划 ==========
        long step2Start = System.currentTimeMillis();
        
        Set<String> schoolUuids = cutoffScores.stream()
            .map(AiAdmissionCutoffScoreDO::getSchoolUuid)
            .filter(StrUtil::isNotBlank)
            .collect(Collectors.toSet());
        
        List<String> targetSchoolUuids = new ArrayList<>(schoolUuids);
        List<CollegeEnrollmentPlanDO> enrollmentPlans = 
            queryEnrollmentPlansBySchoolUuids(targetSchoolUuids, reqVO);
        
        log.info("步骤2-查询招生计划：{}所学校，{}条计划，耗时：{}ms", 
                targetSchoolUuids.size(), enrollmentPlans.size(), System.currentTimeMillis() - step2Start);
        
        // ========== 第3步：按原流程处理（数据量已大幅减少）==========
        long step3Start = System.currentTimeMillis();
        Map<String, List<CollegeEnrollmentPlanDO>> groupedPlans = groupBySchoolAndMajorGroup(enrollmentPlans);
        log.info("步骤3-聚合专业组：{} 个，耗时：{}ms", 
                groupedPlans.size(), System.currentTimeMillis() - step3Start);
        
        // ========== 第4步：并行查询历史数据（数据量小，速度快）==========
        long step4Start = System.currentTimeMillis();
        
        CompletableFuture<Map<String, List<MajorAdmissionDO>>> historyAdmissionsFuture = 
                CompletableFuture.supplyAsync(() -> queryHistoryAdmissions(groupedPlans, reqVO), executorService);
        CompletableFuture<Map<String, Map<Integer, Integer>>> historyEnrollmentNumsFuture = 
                CompletableFuture.supplyAsync(() -> queryHistoryEnrollmentNumbers(groupedPlans, reqVO), executorService);
        CompletableFuture<Map<String, CollegeDO>> collegeInfoFuture = 
                CompletableFuture.supplyAsync(() -> queryCollegeInfo(groupedPlans), executorService);

        try {
            CompletableFuture.allOf(historyAdmissionsFuture, historyEnrollmentNumsFuture, collegeInfoFuture)
                    .get(20, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.warn("步骤4-并行查询超时或异常: {}", e.getMessage());
        }

        Map<String, List<MajorAdmissionDO>> historyAdmissions = getFutureResult(historyAdmissionsFuture, new HashMap<>());
        Map<String, Map<Integer, Integer>> historyEnrollmentNums = getFutureResult(historyEnrollmentNumsFuture, new HashMap<>());
        Map<String, CollegeDO> collegeInfoMap = getFutureResult(collegeInfoFuture, new HashMap<>());
        
        log.info("步骤4-并行查询完成（历史录取/招生人数/高校信息），耗时：{}ms", 
                System.currentTimeMillis() - step4Start);

        // ========== 第5步：构建推荐列表 ==========
        long step5Start = System.currentTimeMillis();
        List<AiVolunteerRecommendItemVO> recommendations = buildRecommendations(
                groupedPlans, historyAdmissions, historyEnrollmentNums, collegeInfoMap, reqVO);
        log.info("步骤5-构建推荐列表：{} 条，耗时：{}ms", 
                recommendations.size(), System.currentTimeMillis() - step5Start);

        // ========== 第6步：应用筛选条件 ==========
        long step6Start = System.currentTimeMillis();
        recommendations = applyFilters(recommendations, reqVO);
        log.info("步骤6-筛选后处理：{} 条，耗时：{}ms",
                recommendations.size(), System.currentTimeMillis() - step6Start);

        // ========== 第7步：排序 ==========
        long step7Start = System.currentTimeMillis();
        recommendations = sortByScore(recommendations, reqVO.getYear());
        log.info("步骤7-按{}年分数排序完成：{} 条，耗时：{}ms", 
                reqVO.getYear(), recommendations.size(), System.currentTimeMillis() - step7Start);
        
        // ========== 第8步：构建响应 ==========
        long step8Start = System.currentTimeMillis();
        AiVolunteerRecommendRespVO respVO = buildResponse(recommendations, (long) recommendations.size(), reqVO);
        log.info("步骤8-构建响应完成，total={}，耗时：{}ms", 
                recommendations.size(), System.currentTimeMillis() - step8Start);

        long totalTime = System.currentTimeMillis() - startTime;
        log.info("AI智能推荐完成，总耗时：{}ms", totalTime);

        return respVO;
    }

    /**
     * 【常规流程】推荐志愿
     */
    private AiVolunteerRecommendRespVO recommendVolunteersNormal(
            AiVolunteerRecommendReqVO reqVO, long startTime) {

        // 1. 查询当年招生计划
        List<CollegeEnrollmentPlanDO> enrollmentPlans = queryEnrollmentPlans(reqVO);
        log.info("步骤1-查询招生计划：{} 条，耗时：{}ms", 
                enrollmentPlans.size(), System.currentTimeMillis() - startTime);

        // 2. 按学校和专业组聚合
        long step2Start = System.currentTimeMillis();
        Map<String, List<CollegeEnrollmentPlanDO>> groupedPlans = groupBySchoolAndMajorGroup(enrollmentPlans);
        log.info("步骤2-聚合专业组：{} 个，耗时：{}ms", 
                groupedPlans.size(), System.currentTimeMillis() - step2Start);

        // 3. 并行查询历史数据
        long step3Start = System.currentTimeMillis();
        
        CompletableFuture<Map<String, List<MajorAdmissionDO>>> historyAdmissionsFuture = 
                CompletableFuture.supplyAsync(() -> queryHistoryAdmissions(groupedPlans, reqVO), executorService);
        
        CompletableFuture<Map<String, Map<Integer, Integer>>> historyEnrollmentNumsFuture = 
                CompletableFuture.supplyAsync(() -> queryHistoryEnrollmentNumbers(groupedPlans, reqVO), executorService);
        
        CompletableFuture<Map<String, CollegeDO>> collegeInfoFuture = 
                CompletableFuture.supplyAsync(() -> queryCollegeInfo(groupedPlans), executorService);

        CompletableFuture.allOf(historyAdmissionsFuture, historyEnrollmentNumsFuture, collegeInfoFuture).join();

        Map<String, List<MajorAdmissionDO>> historyAdmissions = historyAdmissionsFuture.join();
        Map<String, Map<Integer, Integer>> historyEnrollmentNums = historyEnrollmentNumsFuture.join();
        Map<String, CollegeDO> collegeInfoMap = collegeInfoFuture.join();
        
        log.info("步骤3-并行查询完成，耗时：{}ms", System.currentTimeMillis() - step3Start);

        // 4. 构建推荐列表
        long step4Start = System.currentTimeMillis();
        List<AiVolunteerRecommendItemVO> recommendations = buildRecommendations(
                groupedPlans, historyAdmissions, historyEnrollmentNums, collegeInfoMap, reqVO);
        log.info("步骤4-构建推荐列表：{} 条，耗时：{}ms", 
                recommendations.size(), System.currentTimeMillis() - step4Start);

        // 5. 应用筛选条件
        long step5Start = System.currentTimeMillis();
        recommendations = applyFilters(recommendations, reqVO);
        log.info("步骤5-筛选后处理：{} 条，耗时：{}ms",
                recommendations.size(), System.currentTimeMillis() - step5Start);

        // 6. 按当年录取分数排序
        long step6Start = System.currentTimeMillis();
        recommendations = sortByScore(recommendations, reqVO.getYear());
        log.info("步骤6-按{}年分数排序完成，耗时：{}ms", reqVO.getYear(), System.currentTimeMillis() - step6Start);

        // 7. 分页处理
        long step7Start = System.currentTimeMillis();
        AiVolunteerRecommendRespVO respVO = paginate(recommendations, reqVO);
        log.info("步骤7-分页处理完成，耗时：{}ms", System.currentTimeMillis() - step7Start);

        long totalTime = System.currentTimeMillis() - startTime;
        log.info("AI智能推荐完成，总耗时：{}ms", totalTime);

        return respVO;
    }
    
    /**
     * 构建空响应
     */
    private AiVolunteerRecommendRespVO buildEmptyResponse(AiVolunteerRecommendReqVO reqVO) {
        AiVolunteerRecommendRespVO respVO = new AiVolunteerRecommendRespVO();
        respVO.setList(new ArrayList<>());
        respVO.setTotal(0L);
        respVO.setPageNum(reqVO.getPageNum());
        respVO.setPageSize(reqVO.getPageSize());
        respVO.setSize(0);
        respVO.setPages(0);
        return respVO;
    }
    
    /**
     * 构建响应（使用精确的总数）
     */
    private AiVolunteerRecommendRespVO buildResponse(
            List<AiVolunteerRecommendItemVO> recommendations, 
            Long totalCount,
            AiVolunteerRecommendReqVO reqVO) {
        
        AiVolunteerRecommendRespVO respVO = new AiVolunteerRecommendRespVO();
        
        int pageNum = reqVO.getPageNum();
        int pageSize = reqVO.getPageSize();
        
        // 计算实际返回的数据（根据pageNum计算起始位置）
        int startIndex = (pageNum - 1) * pageSize;
        int endIndex = Math.min(startIndex + pageSize, recommendations.size());
        
        // 确保startIndex不超过数据范围
        List<AiVolunteerRecommendItemVO> pageData = new ArrayList<>();
        if (startIndex < recommendations.size()) {
            pageData = recommendations.subList(startIndex, endIndex);
        }
        
        int actualSize = pageData.size();
        
        respVO.setTotal(totalCount);
        respVO.setList(pageData);
        respVO.setPageNum(pageNum);
        respVO.setPageSize(pageSize);
        respVO.setSize(actualSize);
        respVO.setStartRow(startIndex);
        respVO.setEndRow(startIndex + actualSize);
        respVO.setPages((int) ((totalCount + pageSize - 1) / pageSize));
        respVO.setPrePage(pageNum > 1 ? pageNum - 1 : 0);
        respVO.setNextPage(pageNum < respVO.getPages() ? pageNum + 1 : 0);
        respVO.setIsFirstPage(pageNum == 1);
        respVO.setIsLastPage(pageNum >= respVO.getPages());
        respVO.setHasPreviousPage(pageNum > 1);
        respVO.setHasNextPage(pageNum < respVO.getPages());
        respVO.setNavigatePages(8);
        
        // 计算导航页码
        List<Integer> navigatePageNums = new ArrayList<>();
        int navigateFirstPage = Math.max(1, pageNum - 4);
        int navigateLastPage = Math.min(respVO.getPages(), pageNum + 3);
        for (int i = navigateFirstPage; i <= navigateLastPage; i++) {
            navigatePageNums.add(i);
        }
        respVO.setNavigatepageNums(navigatePageNums);
        respVO.setNavigateFirstPage(navigateFirstPage);
        respVO.setNavigateLastPage(navigateLastPage);

        return respVO;
    }

    /**
     * 查询招生计划（优化版：基于AI录取分数线预筛选学校，大幅减少查询范围）
     */
    private List<CollegeEnrollmentPlanDO> queryEnrollmentPlans(AiVolunteerRecommendReqVO reqVO) {
        long startTime = System.currentTimeMillis();
        
        // 【新增优化】如果没有指定学校名称，先从AI录取分数线获取可填报的学校列表
        List<String> eligibleSchools = null;
        if (StrUtil.isBlank(reqVO.getUniversityName()) && 
            (reqVO.getMinScore() != null || reqVO.getMaxScore() != null)) {
            
            long scoreFilterStart = System.currentTimeMillis();
            eligibleSchools = aiAdmissionCutoffScoreMapper.selectSchoolNamesByScoreRange(
                    reqVO.getMinScore(), reqVO.getMaxScore(), reqVO.getClassify(), reqVO.getYear(),reqVO.getProvince(),reqVO.getBatch());
            
            log.info("  ├─ AI录取分数线筛选：从分数范围 [{}-{}] 获取到 {} 所可填报学校，耗时：{}ms", 
                    reqVO.getMinScore(), reqVO.getMaxScore(), 
                    eligibleSchools != null ? eligibleSchools.size() : 0, 
                    System.currentTimeMillis() - scoreFilterStart);
            
            // 如果没有找到符合条件的学校，直接返回空列表
            if (CollUtil.isEmpty(eligibleSchools)) {
                log.info("  └─ 未找到符合分数范围的学校，返回空结果");
                return new ArrayList<>();
            }
        }

        // 构建查询条件
        cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<CollegeEnrollmentPlanDO> wrapper = 
                new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<CollegeEnrollmentPlanDO>()
                        .eq(CollegeEnrollmentPlanDO::getProvinceName, reqVO.getProvince())
                        .eq(CollegeEnrollmentPlanDO::getYear, reqVO.getYear())
                        .eq(CollegeEnrollmentPlanDO::getType, reqVO.getClassify())
                        .eqIfPresent(CollegeEnrollmentPlanDO::getBatchName, reqVO.getBatch());

        // 【优化1】如果指定了学校名称，使用学校名称筛选
        if (StrUtil.isNotBlank(reqVO.getUniversityName())) {
            wrapper.likeIfPresent(CollegeEnrollmentPlanDO::getSchoolName, reqVO.getUniversityName());
        }
        // 【优化2】如果通过AI录取分数线筛选出了学校列表，使用IN条件筛选
        else if (CollUtil.isNotEmpty(eligibleSchools)) {
            wrapper.in(CollegeEnrollmentPlanDO::getSchoolName, eligibleSchools);
        }

        // 【新增】专业名称筛选
        if (StrUtil.isNotBlank(reqVO.getMajor())) {
            wrapper.likeIfPresent(CollegeEnrollmentPlanDO::getCollegeMajorName, reqVO.getMajor());
        }

        long queryStart = System.currentTimeMillis();
        List<CollegeEnrollmentPlanDO> plans = enrollmentPlanMapper.selectList(wrapper);
        log.info("  ├─ 招生计划数据库查询：获取到 {} 条记录，耗时：{}ms", 
                plans.size(), System.currentTimeMillis() - queryStart);

        // 【优化3】选科过滤：并行处理提升性能
        if (StrUtil.isNotBlank(reqVO.getSubjects())) {
            long subjectFilterStart = System.currentTimeMillis();
            List<String> userSubjects = Arrays.asList(reqVO.getSubjects().split(","));
            int originalSize = plans.size();
            plans = plans.parallelStream()  // 使用parallelStream并行处理
                    .filter(plan -> matchSubjectRequirements(plan.getCourseSelectionRequirements(), userSubjects))
                    .collect(Collectors.toList());
            log.info("  ├─ 选科要求筛选：从 {} 条筛选到 {} 条，耗时：{}ms", 
                    originalSize, plans.size(), System.currentTimeMillis() - subjectFilterStart);
        }

        log.info("  └─ 招生计划查询总耗时：{}ms", System.currentTimeMillis() - startTime);
        return plans;
    }

    /**
     * 【提前分页优化】根据学校名称列表查询招生计划
     * 用于提前分页后，只查询需要的学校数据
     * 
     * @param schoolNames 学校名称列表
     * @param reqVO 查询条件
     * @return 招生计划列表
     */
    /**
     * 【优化】使用school_uuid查询招生计划
     * 相比学校名称匹配，UUID匹配更精确且性能更好
     */
    private List<CollegeEnrollmentPlanDO> queryEnrollmentPlansBySchoolUuids(
            List<String> schoolUuids, AiVolunteerRecommendReqVO reqVO) {
        
        if (CollUtil.isEmpty(schoolUuids)) {
            return new ArrayList<>();
        }
        
        long queryStart = System.currentTimeMillis();
        List<CollegeEnrollmentPlanDO> plans = enrollmentPlanMapper.selectList(
                new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<CollegeEnrollmentPlanDO>()
                        .eq(CollegeEnrollmentPlanDO::getProvinceName, reqVO.getProvince())
                        .eq(CollegeEnrollmentPlanDO::getYear, reqVO.getYear())
                        .eq(CollegeEnrollmentPlanDO::getType, reqVO.getClassify())
                        .eqIfPresent(CollegeEnrollmentPlanDO::getBatchName, reqVO.getBatch())
                        .in(CollegeEnrollmentPlanDO::getSchoolUuid, schoolUuids)
                        .likeIfPresent(CollegeEnrollmentPlanDO::getCollegeMajorName, reqVO.getMajor())
        );
        
        log.info("  └─ 根据{}所学校UUID查询招生计划（优化版）：获取到 {} 条记录，耗时：{}ms", 
                schoolUuids.size(), plans.size(), System.currentTimeMillis() - queryStart);
        
        return plans;
    }
    
    /**
     * 【已废弃】使用school_name查询招生计划（保留用于兼容）
     */
    @Deprecated
    private List<CollegeEnrollmentPlanDO> queryEnrollmentPlansBySchools(
            List<String> schoolNames, AiVolunteerRecommendReqVO reqVO) {
        
        if (CollUtil.isEmpty(schoolNames)) {
            return new ArrayList<>();
        }
        
        long queryStart = System.currentTimeMillis();
        List<CollegeEnrollmentPlanDO> plans = enrollmentPlanMapper.selectList(
                new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<CollegeEnrollmentPlanDO>()
                        .eq(CollegeEnrollmentPlanDO::getProvinceName, reqVO.getProvince())
                        .eq(CollegeEnrollmentPlanDO::getYear, reqVO.getYear())
                        .eq(CollegeEnrollmentPlanDO::getType, reqVO.getClassify())
                        .eqIfPresent(CollegeEnrollmentPlanDO::getBatchName, reqVO.getBatch())
                        .in(CollegeEnrollmentPlanDO::getSchoolName, schoolNames)
                        .likeIfPresent(CollegeEnrollmentPlanDO::getCollegeMajorName, reqVO.getMajor())
        );
        
        log.info("  └─ 根据{}所学校查询招生计划：获取到 {} 条记录，耗时：{}ms", 
                schoolNames.size(), plans.size(), System.currentTimeMillis() - queryStart);
        
        // 选科过滤
        if (StrUtil.isNotBlank(reqVO.getSubjects())) {
            long subjectFilterStart = System.currentTimeMillis();
            List<String> userSubjects = Arrays.asList(reqVO.getSubjects().split(","));
            int originalSize = plans.size();
            plans = plans.parallelStream()
                    .filter(plan -> matchSubjectRequirements(plan.getCourseSelectionRequirements(), userSubjects))
                    .collect(Collectors.toList());
            log.info("  └─ 选科要求筛选：从 {} 条筛选到 {} 条，耗时：{}ms", 
                    originalSize, plans.size(), System.currentTimeMillis() - subjectFilterStart);
        }
        
        return plans;
    }

    /**
     * 匹配选科要求
     * 完整支持所有选科格式
     */
    private boolean matchSubjectRequirements(String requirements, List<String> userSubjects) {
        if (StrUtil.isBlank(requirements)) {
            return true;
        }

        // 不限
        if (requirements.contains("不限") || requirements.contains("无要求")) {
            return true;
        }

        // 标准化用户选科（转换为Set便于查找）
        Set<String> userSubjectSet = new HashSet<>(userSubjects);
        
        // 科目简称映射
        Map<String, String> subjectMap = new HashMap<>();
        subjectMap.put("物", "物理");
        subjectMap.put("化", "化学");
        subjectMap.put("生", "生物");
        subjectMap.put("史", "历史");
        subjectMap.put("政", "思想政治");
        subjectMap.put("地", "地理");
        
        // 处理"首选XX，再选YY"格式
        if (requirements.contains("首选") && requirements.contains("再选")) {
            return matchNewGaoKaoFormat(requirements, userSubjectSet);
        }
        
        // 提取要求中的科目（支持全称和简称）
        List<String> requiredSubjects = extractSubjects(requirements, subjectMap);
        
        if (requiredSubjects.isEmpty()) {
            // 无法识别科目，返回true避免误过滤
            return true;
        }
        
        // 判断选科要求类型
        if (requirements.contains("必选")) {
            // X必选 或 X、Y(N科必选)：用户必须选了所有要求的科目
            return requiredSubjects.stream().allMatch(userSubjectSet::contains);
        } else if (requirements.matches(".*\\d+选1.*")) {
            // X/Y/Z(3选1)：用户至少选了其中一个
            return requiredSubjects.stream().anyMatch(userSubjectSet::contains);
        } else {
            // 默认：至少匹配一个（兼容旧格式）
            return requiredSubjects.stream().anyMatch(userSubjectSet::contains);
        }
    }
    
    /**
     * 处理新高考"首选XX，再选YY"格式
     */
    private boolean matchNewGaoKaoFormat(String requirements, Set<String> userSubjectSet) {
        String[] parts = requirements.split("[，,]");
        
        // 检查首选科目
        if (parts.length > 0) {
            String firstChoice = parts[0].replace("首选", "").trim();
            boolean hasFirstChoice = userSubjectSet.stream()
                    .anyMatch(subject -> firstChoice.contains(subject) || subject.contains(firstChoice));
            if (!hasFirstChoice) {
                return false;
            }
        }
        
        // 检查再选科目
        if (parts.length > 1) {
            String secondChoice = parts[1];
            
            // 再选不限
            if (secondChoice.contains("不限")) {
                return true;
            }
            
            // 提取再选科目
            Map<String, String> subjectMap = new HashMap<>();
            subjectMap.put("思想政治", "思想政治");
            subjectMap.put("政治", "思想政治");
            subjectMap.put("物理", "物理");
            subjectMap.put("化学", "化学");
            subjectMap.put("生物", "生物");
            subjectMap.put("历史", "历史");
            subjectMap.put("地理", "地理");
            
            List<String> requiredSubjects = new ArrayList<>();
            for (String subject : subjectMap.keySet()) {
                if (secondChoice.contains(subject)) {
                    String standardName = subjectMap.get(subject);
                    if (!requiredSubjects.contains(standardName)) {
                        requiredSubjects.add(standardName);
                    }
                }
            }
            
            if (requiredSubjects.isEmpty()) {
                return true; // 无法识别，默认通过
            }
            
            // 判断要求类型
            if (secondChoice.contains("必选") || secondChoice.contains("2选2") || secondChoice.contains("3选3")) {
                // 必选：用户必须选了所有科目
                return requiredSubjects.stream().allMatch(userSubjectSet::contains);
            } else if (secondChoice.contains("选1") || secondChoice.contains("2选1") || secondChoice.contains("3选1")) {
                // N选1：用户至少选了其中一个
                return requiredSubjects.stream().anyMatch(userSubjectSet::contains);
            } else {
                // 只提到科目名称，默认为单选其中一个即可
                return requiredSubjects.stream().anyMatch(userSubjectSet::contains);
            }
        }
        
        return true;
    }
    
    /**
     * 从选科要求中提取科目列表
     * 支持全称和简称
     */
    private List<String> extractSubjects(String requirements, Map<String, String> subjectMap) {
        List<String> subjects = new ArrayList<>();
        
        // 先提取全称
        String[] fullNames = {"思想政治", "物理", "化学", "生物", "历史", "地理"};
        for (String subject : fullNames) {
            if (requirements.contains(subject)) {
                subjects.add(subject);
            }
        }
        
        // 如果没找到全称，尝试提取简称
        if (subjects.isEmpty()) {
            // 移除括号内容，避免误匹配
            String cleanReq = requirements.replaceAll("\\([^)]*\\)", "");
            
            // 提取简称（物、化、生等）
            for (Map.Entry<String, String> entry : subjectMap.entrySet()) {
                String shortName = entry.getKey();
                String fullName = entry.getValue();
                
                // 使用正则确保是独立的字（不是其他词的一部分）
                if (cleanReq.matches(".*[、/,，]?" + shortName + "[、/,，]?.*") ||
                    cleanReq.startsWith(shortName) || cleanReq.endsWith(shortName)) {
                    if (!subjects.contains(fullName)) {
                        subjects.add(fullName);
                    }
                }
            }
        }
        
        return subjects;
    }

    /**
     * 按学校和专业组聚合
     * 注意：使用专业组名称中的核心标识（括号内容）进行分组，因为special_group（六位数ID）每年会变化
     */
    private Map<String, List<CollegeEnrollmentPlanDO>> groupBySchoolAndMajorGroup(
            List<CollegeEnrollmentPlanDO> enrollmentPlans) {
        return enrollmentPlans.stream()
                .collect(Collectors.groupingBy(plan ->
                        plan.getSchoolUuid() + "_" + extractGroupIdentifier(plan.getSpecialGroupName())));
    }
    
    /**
     * 提取专业组标识符（从括号中提取，如 "2267（L001）" -> "001"，"（001）" -> "001"）
     * 用于跨年度匹配专业组，因为special_group（六位数ID）每年会变化
     * 注意：会去掉字母前缀（L、W等），只保留数字部分以支持跨年度匹配
     */
    private String extractGroupIdentifier(String specialGroupName) {
        if (StrUtil.isBlank(specialGroupName)) {
            return "default";
        }
        
        // 匹配中文括号或英文括号中的内容
        // 示例：2267（L001）-> L001，（001）-> 001，(W001) -> W001
        String pattern = ".*[（(]([^）)]+)[）)].*";  // 提取括号内容
        String extracted = specialGroupName;
        
        if (specialGroupName.matches(pattern)) {
            extracted = specialGroupName.replaceAll(pattern, "$1");
        }
        
        // 去掉字母前缀（L、W、H等），只保留数字部分
        // L001 -> 001, W003 -> 003, 001 -> 001
        String normalized = extracted.replaceAll("^[A-Z]+", "");
        
        if (!extracted.equals(normalized)) {
            log.debug("提取并规范化专业组标识符：{} -> {} -> {}", specialGroupName, extracted, normalized);
        } else {
            log.debug("提取专业组标识符：{} -> {}", specialGroupName, normalized);
        }
        
        return normalized;
    }

    /**
     * 查询历史录取数据（分批查询优化，避免IN条件过多导致性能下降）
     */
    private Map<String, List<MajorAdmissionDO>> queryHistoryAdmissions(
            Map<String, List<CollegeEnrollmentPlanDO>> groupedPlans, AiVolunteerRecommendReqVO reqVO) {
        
        long queryStart = System.currentTimeMillis();
        
        // 收集所有学校UUID
        Set<String> schoolUuids = new HashSet<>();
        for (List<CollegeEnrollmentPlanDO> plans : groupedPlans.values()) {
            if (!plans.isEmpty()) {
                schoolUuids.add(plans.get(0).getSchoolUuid());
            }
        }

        if (schoolUuids.isEmpty()) {
            return new HashMap<>();
        }

        // 【性能优化】只查询近1年的历史数据
        List<Integer> historyYears = Arrays.asList(reqVO.getYear() - 1);

        log.info("    准备查询历史录取数据：学校{}个，年份{}，批次={}，类型={}", 
                schoolUuids.size(), historyYears, reqVO.getBatch(), reqVO.getClassify());

        // 【性能优化】增大批次大小并并行查询
        List<String> schoolUuidList = new ArrayList<>(schoolUuids);
        int batchSize = 500;  // 从300增加到500
        int totalBatches = (schoolUuidList.size() + batchSize - 1) / batchSize;
        
        // 【关键优化】使用并行查询，多个批次同时执行
        List<CompletableFuture<List<MajorAdmissionDO>>> futures = new ArrayList<>();
        
        for (int i = 0; i < totalBatches; i++) {
            int fromIndex = i * batchSize;
            int toIndex = Math.min((i + 1) * batchSize, schoolUuidList.size());
            List<String> batchUuids = schoolUuidList.subList(fromIndex, toIndex);
            int batchIndex = i + 1;
            
            CompletableFuture<List<MajorAdmissionDO>> future = CompletableFuture.supplyAsync(() -> {
                long batchStart = System.currentTimeMillis();
                List<MajorAdmissionDO> batchResult = majorAdmissionMapper.selectList(
                        new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<MajorAdmissionDO>()
                                .in(MajorAdmissionDO::getSchoolUuid, batchUuids)
                                .eq(MajorAdmissionDO::getProvinceName, reqVO.getProvince())
                                .in(MajorAdmissionDO::getYear, historyYears)
                                .eqIfPresent(MajorAdmissionDO::getBatchName, reqVO.getBatch())
                                .eq(MajorAdmissionDO::getTypeName, reqVO.getClassify())
                );
                
                log.info("      批次{}/{}：查询{}个学校，获得{}条数据，耗时{}ms", 
                        batchIndex, totalBatches, batchUuids.size(), batchResult.size(), 
                        System.currentTimeMillis() - batchStart);
                
                return batchResult;
            }, ioExecutor);
            
            futures.add(future);
        }
        
        // 等待所有批次完成并合并结果
        List<MajorAdmissionDO> allAdmissions = futures.stream()
                .map(CompletableFuture::join)
                .flatMap(List::stream)
                .collect(Collectors.toList());

        long queryTime = System.currentTimeMillis() - queryStart;
        log.info("    历史录取数据并行查询完成：{}条数据，总耗时{}ms", allAdmissions.size(), queryTime);

        // 在内存中按 schoolUuid 分组
        Map<String, List<MajorAdmissionDO>> result = allAdmissions.stream()
                .collect(Collectors.groupingBy(MajorAdmissionDO::getSchoolUuid));

        return result;
    }

    /**
     * 查询历史招生人数（并行分批查询优化）
     * 返回格式：Map<学校UUID_专业组, Map<年份, 招生人数>>
     */
    private Map<String, Map<Integer, Integer>> queryHistoryEnrollmentNumbers(
            Map<String, List<CollegeEnrollmentPlanDO>> groupedPlans, AiVolunteerRecommendReqVO reqVO) {
        
        long queryStart = System.currentTimeMillis();
        
        // 收集所有学校UUID
        Set<String> schoolUuids = new HashSet<>();
        for (List<CollegeEnrollmentPlanDO> plans : groupedPlans.values()) {
            if (!plans.isEmpty()) {
                schoolUuids.add(plans.get(0).getSchoolUuid());
            }
        }

        if (schoolUuids.isEmpty()) {
            return new HashMap<>();
        }

        // 【性能优化】只查询近1年的历史招生计划数据
        List<Integer> historyYears = Arrays.asList(reqVO.getYear() - 1);

        log.info("    准备查询历史招生人数：学校{}个，年份{}，批次={}，类型={}", 
                schoolUuids.size(), historyYears, reqVO.getBatch(), reqVO.getClassify());

        // 【性能优化】增大批次大小并并行查询
        List<String> schoolUuidList = new ArrayList<>(schoolUuids);
        int batchSize = 500;  // 从300增加到500
        int totalBatches = (schoolUuidList.size() + batchSize - 1) / batchSize;
        
        // 【关键优化】使用并行查询，多个批次同时执行
        List<CompletableFuture<List<CollegeEnrollmentPlanDO>>> futures = new ArrayList<>();
        
        for (int i = 0; i < totalBatches; i++) {
            int fromIndex = i * batchSize;
            int toIndex = Math.min((i + 1) * batchSize, schoolUuidList.size());
            List<String> batchUuids = schoolUuidList.subList(fromIndex, toIndex);
            int batchIndex = i + 1;
            
            CompletableFuture<List<CollegeEnrollmentPlanDO>> future = CompletableFuture.supplyAsync(() -> {
                long batchStart = System.currentTimeMillis();
                List<CollegeEnrollmentPlanDO> batchResult = enrollmentPlanMapper.selectList(
                        new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<CollegeEnrollmentPlanDO>()
                                .in(CollegeEnrollmentPlanDO::getSchoolUuid, batchUuids)
                                .eq(CollegeEnrollmentPlanDO::getProvinceName, reqVO.getProvince())
                                .eq(CollegeEnrollmentPlanDO::getType, reqVO.getClassify())
                                .in(CollegeEnrollmentPlanDO::getYear, historyYears)
                                .eqIfPresent(CollegeEnrollmentPlanDO::getBatchName, reqVO.getBatch())
                );
                
                log.info("      批次{}/{}：查询{}个学校，获得{}条数据，耗时{}ms", 
                        batchIndex, totalBatches, batchUuids.size(), batchResult.size(), 
                        System.currentTimeMillis() - batchStart);
                
                return batchResult;
            }, ioExecutor);
            
            futures.add(future);
        }
        
        // 等待所有批次完成并合并结果
        List<CollegeEnrollmentPlanDO> historyPlans = futures.stream()
                .map(CompletableFuture::join)
                .flatMap(List::stream)
                .collect(Collectors.toList());

        long queryTime = System.currentTimeMillis() - queryStart;
        log.info("    历史招生人数并行查询完成：{}条数据，总耗时{}ms", historyPlans.size(), queryTime);

        // 按 学校UUID_专业组 和 年份 聚合招生人数
        Map<String, Map<Integer, Integer>> result = new HashMap<>();
        
        for (CollegeEnrollmentPlanDO plan : historyPlans) {
            // 使用专业组名称的核心标识符（括号内容）构建key，以支持跨年度匹配
            String key = plan.getSchoolUuid() + "_" + extractGroupIdentifier(plan.getSpecialGroupName());
            
            result.putIfAbsent(key, new HashMap<>());
            Map<Integer, Integer> yearMap = result.get(key);
            
            // 累加同一年份的招生人数
            try {
                int enrollNum = Integer.parseInt(plan.getEnrollmentNumbers());
                yearMap.put(plan.getYear(), yearMap.getOrDefault(plan.getYear(), 0) + enrollNum);
            } catch (Exception e) {
                // 忽略解析错误
            }
        }

        return result;
    }

    /**
     * 查询高校基础信息（使用Redis缓存）
     * 
     * 【第二阶段优化】使用缓存服务，减少数据库查询
     * 缓存命中时可节省 50-100ms
     */
    private Map<String, CollegeDO> queryCollegeInfo(Map<String, List<CollegeEnrollmentPlanDO>> groupedPlans) {
        long queryStart = System.currentTimeMillis();
        
        // 收集所有学校UUID
        Set<String> schoolUuids = new HashSet<>();
        for (List<CollegeEnrollmentPlanDO> plans : groupedPlans.values()) {
            if (!plans.isEmpty()) {
                schoolUuids.add(plans.get(0).getSchoolUuid());
            }
        }

        if (schoolUuids.isEmpty()) {
            return new HashMap<>();
        }

        log.info("    准备查询高校信息：学校{}个（使用Redis缓存）", schoolUuids.size());

        // 【第二阶段优化】使用缓存服务，自动处理Redis缓存逻辑
        List<String> schoolUuidList = new ArrayList<>(schoolUuids);
        Map<String, CollegeDO> result = cacheService.getCollegeInfo(schoolUuidList);

        log.info("    高校信息查询完成：{}条数据，耗时{}ms", result.size(), System.currentTimeMillis() - queryStart);

        // 转换key：从id转为schoolUuid
        Map<String, CollegeDO> collegeByUuidMap = new HashMap<>();
        for (CollegeDO college : result.values()) {
            collegeByUuidMap.put(college.getSchoolUuid(), college);
        }

        return collegeByUuidMap;
    }

    private <T> T getFutureResult(CompletableFuture<T> future, T fallback) {
        try {
            return future.get(10, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.warn("并行查询异常: {}", e.getMessage());
            return fallback;
        }
    }

    /**
     * 【性能优化】批量预加载AI录取分数线数据（使用Redis缓存）
     * 
     * 【第二阶段优化】使用缓存服务减少数据库查询
     * 缓存命中时可节省 30-50ms
     */
    private Map<String, AiAdmissionCutoffScoreDO> preloadCutoffScores(
            Map<String, List<CollegeEnrollmentPlanDO>> groupedPlans,
            AiVolunteerRecommendReqVO reqVO) {
        
        Map<String, AiAdmissionCutoffScoreDO> cutoffScoreMap = new HashMap<>();
        long batchLoadStart = System.currentTimeMillis();
        
        try {
            // 【优化】收集所有学校UUID，使用精确匹配
            Set<String> schoolUuids = new HashSet<>();
            
            for (List<CollegeEnrollmentPlanDO> plans : groupedPlans.values()) {
                if (!plans.isEmpty()) {
                    String schoolUuid = plans.get(0).getSchoolUuid();
                    if (StrUtil.isNotBlank(schoolUuid)) {
                        schoolUuids.add(schoolUuid);
                    }
                }
            }
            
            if (!schoolUuids.isEmpty()) {
                log.info("  开始批量预加载AI录取分数线：学校UUID数={}, 年份={}, 类型={}, 省份={}, 批次={}", 
                        schoolUuids.size(), 
                        reqVO.getYear(), reqVO.getClassify(), reqVO.getProvince(), reqVO.getBatch());
                
                // 【优化】使用school_uuid查询AI录取分数线
                List<AiAdmissionCutoffScoreDO> cutoffScores = cacheService.getCutoffScoresByUuids(
                        new ArrayList<>(schoolUuids), reqVO.getYear(), reqVO.getClassify(),reqVO.getProvince(),reqVO.getBatch());
                
                // 选科要求过滤
                if (StrUtil.isNotBlank(reqVO.getSubjects())) {
                    int beforeFilter = cutoffScores.size();
                    List<String> userSubjects = Arrays.asList(reqVO.getSubjects().split(","));
                    
                    cutoffScores = cutoffScores.stream()
                        .filter(cutoff -> matchSubjectRequirementsForCount(cutoff.getSubjectRequirement(), userSubjects))
                        .collect(Collectors.toList());
                    
                    log.info("  批量预加载AI录取分数线-选科过滤：从 {} 条筛选到 {} 条", beforeFilter, cutoffScores.size());
                }
                
                // 【优化】构建Map：key = "schoolUuid_专业组"，使用UUID精确匹配
                for (AiAdmissionCutoffScoreDO score : cutoffScores) {
                    if (StrUtil.isNotBlank(score.getSchoolUuid())) {
                        String key = score.getSchoolUuid() + "_" + score.getGroupName();
                        cutoffScoreMap.put(key, score);
                    }
                }
                
                log.info("  批量预加载AI录取分数线：查询{}所学校，获得{}条数据，构建{}个key，耗时{}ms（使用Redis缓存+UUID精确匹配）", 
                        schoolUuids.size(), 
                        cutoffScores.size(), cutoffScoreMap.size(), System.currentTimeMillis() - batchLoadStart);
                
                // 【调试】输出前5个key的格式
                if (!cutoffScoreMap.isEmpty()) {
                    String sampleKeys = cutoffScoreMap.keySet().stream()
                            .limit(5)
                            .collect(Collectors.joining(", "));
                    log.info("  批量预加载AI录取分数线：构建的key示例：{}", sampleKeys);
                }
            }
        } catch (Exception e) {
            log.warn("批量预加载AI录取分数线失败：{}", e.getMessage());
        }
        
        return cutoffScoreMap;
    }

    /**
     * 构建推荐列表（优化版：并行处理 + 提前筛选）
     */
    private List<AiVolunteerRecommendItemVO> buildRecommendations(
            Map<String, List<CollegeEnrollmentPlanDO>> groupedPlans,
            Map<String, List<MajorAdmissionDO>> historyAdmissions,
            Map<String, Map<Integer, Integer>> historyEnrollmentNums,
            Map<String, CollegeDO> collegeInfoMap,
            AiVolunteerRecommendReqVO reqVO) {

        // 【性能优化】批量预加载AI录取分数线数据，避免N+1查询问题
        Map<String, AiAdmissionCutoffScoreDO> cutoffScoreMap = preloadCutoffScores(groupedPlans, reqVO);

        // 【第二阶段优化】使用并行流处理专业组，提升性能30-40%
        List<AiVolunteerRecommendItemVO> recommendations = groupedPlans.entrySet()
                .parallelStream()
                .map(entry -> buildSingleRecommendation(
                        entry.getKey(),
                        entry.getValue(),
                        historyAdmissions,
                        historyEnrollmentNums,
                        collegeInfoMap,
                        cutoffScoreMap,
                        reqVO))
                .filter(item -> item != null)
                .collect(Collectors.toList());

        return recommendations;
    }

    /**
     * 构建单个专业组的推荐信息（线程安全）
     * 
     * @return 推荐信息，如果不符合筛选条件则返回null
     */
    private AiVolunteerRecommendItemVO buildSingleRecommendation(
            String groupKey,
            List<CollegeEnrollmentPlanDO> plans,
            Map<String, List<MajorAdmissionDO>> historyAdmissions,
            Map<String, Map<Integer, Integer>> historyEnrollmentNums,
            Map<String, CollegeDO> collegeInfoMap,
            Map<String, AiAdmissionCutoffScoreDO> cutoffScoreMap,
            AiVolunteerRecommendReqVO reqVO) {

        if (plans.isEmpty()) {
            return null;
        }

        CollegeEnrollmentPlanDO firstPlan = plans.get(0);
        String schoolUuid = firstPlan.getSchoolUuid();
        
        // 【重要限制】只推荐AI录取分数线表中有的专业组
        // 检查该专业组是否在cutoffScoreMap中存在
        // cutoffScoreMap的key格式是 "schoolUuid_专业组"
        boolean foundInCutoffScores = false;
        if (StrUtil.isNotBlank(schoolUuid)) {
            String majorGroupDisplay = firstPlan.getSpecialGroupName();
            if (StrUtil.isBlank(majorGroupDisplay)) {
                majorGroupDisplay = firstPlan.getSpecialGroup();
            }
            
            // 尝试多种专业组格式
            if (StrUtil.isNotBlank(majorGroupDisplay)) {
                String[] groupNameVariants = new String[] {
                    majorGroupDisplay,  // 原始格式
                    majorGroupDisplay.replace("（", "").replace("）", "").replace("(", "").replace(")", ""),  // 去掉括号
                    majorGroupDisplay.replace("（", "").replace("）", "").replace("(", "").replace(")", "").replaceAll("^[A-Z]+", "")  // 去掉字母前缀
                };
                
                for (String variant : groupNameVariants) {
                    String key = schoolUuid + "_" + variant;
                    if (cutoffScoreMap.containsKey(key)) {
                        foundInCutoffScores = true;
                        break;
                    }
                }
            }
        }
        
        // 如果不在AI录取分数线表中，不推荐
        if (!foundInCutoffScores) {
            return null;
        }
        
        // 【优化1】提前应用学校名称筛选，避免构建后续会被丢弃的数据
        if (StrUtil.isNotBlank(reqVO.getUniversityName())) {
            if (StrUtil.isBlank(firstPlan.getSchoolName()) || 
                !firstPlan.getSchoolName().contains(reqVO.getUniversityName())) {
                return null;
            }
        }
        
        // 【优化2】提前获取学校信息并应用筛选
        CollegeDO college = collegeInfoMap.get(schoolUuid);
        if (college != null) {
            // 学校性质筛选
            if (StrUtil.isNotBlank(reqVO.getPropertyName()) && 
                !reqVO.getPropertyName().equals(college.getCollegeType())) {
                return null;
            }
            
            // 学校类别筛选
            if (StrUtil.isNotBlank(reqVO.getCategoryName()) && 
                !reqVO.getCategoryName().equals(college.getCollegeCategory())) {
                return null;
            }
            
            // 城市筛选
            if (CollUtil.isNotEmpty(reqVO.getCitys()) && 
                !reqVO.getCitys().contains(college.getCity())) {
                return null;
            }
            
            // 省份筛选
            if (CollUtil.isNotEmpty(reqVO.getProvinces()) && 
                !reqVO.getProvinces().contains(college.getProvince())) {
                return null;
            }
            
            // 学校层次筛选（universityLevel）
            if (StrUtil.isNotBlank(reqVO.getUniversityLevel())) {
                // 根据学校层次筛选，如985、211、双一流等
                boolean levelMatch = false;
                switch (reqVO.getUniversityLevel()) {
                    case "985":
                        levelMatch = Boolean.TRUE.equals(college.getIs985());
                        break;
                    case "211":
                        levelMatch = Boolean.TRUE.equals(college.getIs211());
                        break;
                    case "双一流":
                        levelMatch = Boolean.TRUE.equals(college.getIsDualClass());
                        break;
                    case "普通本科":
                        levelMatch = !Boolean.TRUE.equals(college.getIs985()) && 
                                   !Boolean.TRUE.equals(college.getIs211()) && 
                                   !Boolean.TRUE.equals(college.getIsDualClass());
                        break;
                    default:
                        levelMatch = true; // 未知层次不筛选
                        break;
                }
                if (!levelMatch) {
                    return null;
                }
            }
            
            // 学校标签筛选
            if (CollUtil.isNotEmpty(reqVO.getUniversityTagNames()) && 
                CollUtil.isNotEmpty(college.getCollegeTags())) {
                boolean hasTag = reqVO.getUniversityTagNames().stream()
                        .anyMatch(tag -> college.getCollegeTags().contains(tag));
                if (!hasTag) {
                    return null;
                }
            }
            
            // universityTag 单个标签筛选
            if (StrUtil.isNotBlank(reqVO.getUniversityTag()) && 
                CollUtil.isNotEmpty(college.getCollegeTags())) {
                if (!college.getCollegeTags().contains(reqVO.getUniversityTag())) {
                    return null;
                }
            }
        }

        AiVolunteerRecommendItemVO item = new AiVolunteerRecommendItemVO();

        // 基础信息
        item.setUniversityName(firstPlan.getSchoolName());
        item.setOriginUniversityName(firstPlan.getSchoolName());
        item.setUniversityId(schoolUuid);
        item.setYear(reqVO.getYear());
        item.setBatch(firstPlan.getBatchName());
        item.setLevel("本科");

        // 专业组信息
        // 优先使用专业组名称，如果为空则使用专业组代码
        String majorGroupDisplay = StrUtil.isNotBlank(firstPlan.getSpecialGroupName()) 
                ? firstPlan.getSpecialGroupName() 
                : (StrUtil.isNotBlank(firstPlan.getSpecialGroup()) ? firstPlan.getSpecialGroup() : "-");
        item.setUniversityMajorGroup(majorGroupDisplay);
        item.setMajorNum(plans.size());
        item.setClaim(firstPlan.getCourseSelectionRequirements());

        // 招生计划
        int totalPlanNum = plans.stream()
                .mapToInt(p -> {
                    try {
                        return Integer.parseInt(p.getEnrollmentNumbers());
                    } catch (Exception e) {
                        return 0;
                    }
                })
                .sum();
        item.setPlanNum(totalPlanNum);

        // 【优化3】学校信息已在前面获取，直接使用（避免重复查找）
        if (college != null) {
            item.setProvince(college.getProvince());
            item.setPropertyName(college.getCollegeType());
            item.setCategoryName(college.getCollegeCategory());
            item.setLogo(college.getCoverImage());

            // 标签
            if (CollUtil.isNotEmpty(college.getCollegeTags())) {
                item.setTags(String.join(",", college.getCollegeTags()));
            }
        }

        // 【优化4】预先提取专业名称列表和HashSet，避免后续重复遍历
        List<String> majorNamesInGroup = new ArrayList<>();
        Set<String> majorNameSet = new HashSet<>();
        String firstMajorName = null;
        
        for (CollegeEnrollmentPlanDO plan : plans) {
            String majorName = plan.getCollegeMajorName();
            if (StrUtil.isNotBlank(majorName)) {
                String baseName = majorName.split("（")[0].trim();
                majorNamesInGroup.add(baseName);
                majorNameSet.add(baseName);
                if (firstMajorName == null) {
                    firstMajorName = majorName;
                }
            }
        }

        // 历史录取数据
        List<MajorAdmissionDO> allSchoolAdmissions = historyAdmissions.get(schoolUuid);
        Integer lowestScore = null;
        String lowestMajorName = null;
        
        // 【优化5】使用HashSet加速匹配，避免嵌套Stream
        List<MajorAdmissionDO> admissions = new ArrayList<>();
        MajorAdmissionDO tempLowestMajor = null;
        int tempLowestScoreValue = Integer.MAX_VALUE;
        
        if (allSchoolAdmissions != null && !allSchoolAdmissions.isEmpty()) {
            for (MajorAdmissionDO admission : allSchoolAdmissions) {
                String majorName = admission.getMajorName();
                if (StrUtil.isBlank(majorName)) {
                    continue;
                }
                
                // 使用HashSet快速判断是否匹配
                boolean matched = false;
                for (String groupMajor : majorNameSet) {
                    if (majorName.contains(groupMajor) || groupMajor.contains(majorName)) {
                        matched = true;
                        break;
                    }
                }
                
                if (matched) {
                    admissions.add(admission);
                    
                    // 【优化6】同时找出最低分专业，避免后续再次遍历
                    if (StrUtil.isNotBlank(admission.getLowestScore()) && !"-".equals(admission.getLowestScore())) {
                        try {
                            int score = Integer.parseInt(admission.getLowestScore());
                            if (score < tempLowestScoreValue) {
                                tempLowestScoreValue = score;
                                tempLowestMajor = admission;
                            }
                        } catch (Exception e) {
                            // 忽略解析错误
                        }
                    }
                }
            }
            
            // 如果没有匹配到专业组的历史数据，使用全校数据（兜底）
            if (admissions.isEmpty()) {
                admissions = allSchoolAdmissions;
                // 重新查找最低分专业
                tempLowestMajor = null;
                tempLowestScoreValue = Integer.MAX_VALUE;
                for (MajorAdmissionDO admission : allSchoolAdmissions) {
                    if (StrUtil.isNotBlank(admission.getLowestScore()) && !"-".equals(admission.getLowestScore())) {
                        try {
                            int score = Integer.parseInt(admission.getLowestScore());
                            if (score < tempLowestScoreValue) {
                                tempLowestScoreValue = score;
                                tempLowestMajor = admission;
                            }
                        } catch (Exception e) {
                            // 忽略解析错误
                        }
                    }
                }
            }
            
            // 【优化7】使用之前已找到的最低分专业，避免后续再次遍历
            if (tempLowestMajor != null && StrUtil.isNotBlank(tempLowestMajor.getMajorName())) {
                lowestMajorName = tempLowestMajor.getMajorName();
                item.setLowestMajorName(lowestMajorName);
            }
        } else {
            // 即使没有历史录取数据，也设置 admissions 为空列表
            // 因为 buildHistoryScore 方法会尝试从 cutoffScoreMap 获取当年数据
            admissions = new ArrayList<>();
            
            // 【优化9】使用预先提取的专业名称，避免再次遍历
            if (firstMajorName != null) {
                lowestMajorName = firstMajorName;
                item.setLowestMajorName(firstMajorName);
            }
        }
        
        // 【修改】不再过滤没有AI录取分数线的专业组，只记录诊断日志
        // 即使该专业组在 AI录取分数线表中没有对应的数据，也继续构建推荐记录
        // 【优化】使用schoolUuid进行精确匹配
        AiAdmissionCutoffScoreDO typeCheckCutoff = findCutoffScoreFromMap(
                cutoffScoreMap, firstPlan.getSchoolUuid(), majorGroupDisplay, reqVO.getYear());
        
        if (typeCheckCutoff == null) {
            // 该专业组在 AI录取分数线表中没有对应类型的数据，但不过滤，仅记录日志
            // 【关键诊断】记录所有没有AI录取分数线的专业组，以便与 count 接口对比
            String schoolGroup = firstPlan.getSchoolName() + "_" + majorGroupDisplay;
            
            // 检查这个专业组的类型
            if (lowestScore != null && reqVO.getScore() != null) {
                int scoreDiff = lowestScore - reqVO.getScore();
                String expectedType = determineRecommendationTypeByScore(lowestScore, reqVO.getScore());
                
                if ("BAO".equals(expectedType)) {
                    // 这是一个保底类型，虽然在AI录取分数线表中找不到匹配，但仍然保留
                    log.info("【保留-AI表未匹配】{}，录取分数={}，用户分数={}，分数差={}，期望类型=BAO（已保留）", 
                            schoolGroup, lowestScore, reqVO.getScore(), scoreDiff);
                } else {
                    log.debug("【保留-AI表未匹配】{}，录取分数={}，分数差={}，期望类型={}（已保留）", 
                            schoolGroup, lowestScore, scoreDiff, expectedType);
                }
            } else {
                // lowestScore 为 null，无法判断类型
                log.debug("【保留-AI表未匹配且无分数】{}，lowestScore=null（已保留）", schoolGroup);
            }
            
            // 不再返回 null，继续构建推荐记录
        }
        
        // 获取该专业组的历史招生人数（包括当年）
        Map<Integer, Integer> enrollNumsByYear = new HashMap<>(
                historyEnrollmentNums.getOrDefault(groupKey, new HashMap<>()));
        
        // 添加当年的招生人数
        enrollNumsByYear.put(reqVO.getYear(), totalPlanNum);
        
        // 【关键修复】无论是否有历史录取数据，都调用 buildHistoryScore
        // 因为该方法会尝试从 cutoffScoreMap 中获取当年（2025年）的AI录取分数线数据
        // 【优化】传入schoolUuid用于精确匹配
        buildHistoryScore(item, admissions, enrollNumsByYear, reqVO, firstPlan.getSchoolUuid(), firstPlan.getSchoolName(), majorGroupDisplay, cutoffScoreMap);
        
        // 获取专业组的最低分（用于分数区间筛选和推荐类型判定）
        // 优先使用当年（2025年）的分数，如果没有则使用历史年份（2024年）的分数
        // 【优化】使用schoolUuid进行精确匹配
        lowestScore = getLowestScoreForRecommendation(firstPlan.getSchoolUuid(), majorGroupDisplay, admissions, cutoffScoreMap, reqVO.getYear());
        
        // 【调试日志】记录632分的记录
        if (lowestScore != null && lowestScore == 632) {
            log.info("【调试-632分】构建推荐：学校={}，专业组={}，最低分={}, 用户分数={}", 
                    firstPlan.getSchoolName(), majorGroupDisplay, lowestScore, reqVO.getScore());
        }
        
        // 计算录取概率（保留用于展示，但不用于类型判定）
        Integer probability = calculateAdmissionProbability(admissions, reqVO.getRanks(), reqVO.getScore());
        item.setEnrollProbability(probability);

        // 确定推荐类型（基于分数差值）
        String type = determineRecommendationTypeByScore(lowestScore, reqVO.getScore());
        item.setType(type);
        
        // 【优化8】提前应用推荐类型筛选
        if (StrUtil.isNotBlank(reqVO.getType()) && !reqVO.getType().equals(type)) {
            // 【调试】输出所有被type过滤的记录
            log.info("【过滤-type不匹配】学校={}，专业组={}，录取分数={}，用户分数={}，分数差={}，判定类型={}，期望类型={}", 
                    firstPlan.getSchoolName(), majorGroupDisplay, lowestScore, reqVO.getScore(), 
                    (lowestScore != null ? (lowestScore - reqVO.getScore()) : "null"), type, reqVO.getType());
            return null;
        }

        // 分数区间判断和筛选
        // 关键：只要专业组中最低分的专业在区间内，整个专业组都符合要求
        boolean inScoreSection = true;
        if (reqVO.getMinScore() != null && reqVO.getMaxScore() != null && lowestScore != null) {
            // 判断专业组最低分是否在区间内
            inScoreSection = lowestScore >= reqVO.getMinScore() && lowestScore <= reqVO.getMaxScore();
            item.setScoreSection(inScoreSection);
        } else {
            item.setScoreSection(true);
        }

        // 如果设置了分数区间且不在区间内，则跳过这个专业组
        if (reqVO.getMinScore() != null && reqVO.getMaxScore() != null && !inScoreSection) {
            return null;
        }

        // 下一年信息
        item.setNextYear(reqVO.getYear() + 1);
        item.setIndexVip(false);

        return item;
    }

    /**
     * 获取专业组的最低分（从最近有效年份的历史录取数据中）
     * 用于分数区间筛选：找到最近有效年份该专业组所有专业中的最低分
     */
    private Integer getLowestScoreFromAdmissions(List<MajorAdmissionDO> admissions) {
        if (admissions == null || admissions.isEmpty()) {
            return null;
        }

        // 按年份分组
        Map<Integer, List<MajorAdmissionDO>> yearMap = admissions.stream()
                .collect(Collectors.groupingBy(MajorAdmissionDO::getYear));

        // 按年份从新到旧排序
        List<Integer> sortedYears = yearMap.keySet().stream()
                .sorted(Comparator.reverseOrder())
                .collect(Collectors.toList());

        // 从最近的年份开始找，找到第一个有有效分数的年份
        for (Integer year : sortedYears) {
            List<MajorAdmissionDO> yearAdmissions = yearMap.get(year);
            
            // 找该年份该专业组的最低分
            Integer lowestScore = yearAdmissions.stream()
                    .filter(a -> StrUtil.isNotBlank(a.getLowestScore()) && !"-".equals(a.getLowestScore()))
                    .map(a -> {
                        try {
                            return Integer.parseInt(a.getLowestScore());
                        } catch (Exception e) {
                            log.warn("解析最低分失败：{}", a.getLowestScore());
                            return null;
                        }
                    })
                    .filter(score -> score != null)
                    .min(Integer::compareTo)
                    .orElse(null);
            
            // 如果该年份有有效分数，返回
            if (lowestScore != null) {
                if (log.isDebugEnabled()) {
                    log.debug("使用{}年的最低分：{}", year, lowestScore);
                }
                return lowestScore;
            }
        }

        return null;
    }

    /**
     * 获取用于推荐类型判定的最低分
     * 优先使用当年（2025年）的分数，如果没有则使用历史年份（2024年）的分数
     * 
     * @param schoolName 学校名称
     * @param majorGroupName 专业组名称
     * @param admissions 历史录取数据（2024年及之前）
     * @param cutoffScoreMap 预加载的AI录取分数线数据（2025年）
     * @param currentYear 当前年份（如2025）
     * @return 最低分
     */
    private Integer getLowestScoreForRecommendation(String schoolUuid, 
                                                     String majorGroupName,
                                                     List<MajorAdmissionDO> admissions,
                                                     Map<String, AiAdmissionCutoffScoreDO> cutoffScoreMap,
                                                     Integer currentYear) {
        // 1. 优先尝试从AI录取分数线表获取当年（2025年）的分数
        // 【优化】使用schoolUuid进行精确匹配
        AiAdmissionCutoffScoreDO cutoffScore = findCutoffScoreFromMap(
                cutoffScoreMap, schoolUuid, majorGroupName, currentYear);
        
        if (cutoffScore != null && StrUtil.isNotBlank(cutoffScore.getLowerScore()) 
            && !"-".equals(cutoffScore.getLowerScore())) {
            try {
                Integer score = Integer.parseInt(cutoffScore.getLowerScore());
                log.debug("【推荐类型判定】使用{}年AI录取分数线：UUID={}，专业组={}，分数={}", 
                        currentYear, schoolUuid, majorGroupName, score);
                return score;
            } catch (Exception e) {
                log.debug("【推荐类型判定】解析AI录取分数线失败：{}", cutoffScore.getLowerScore());
            }
        }
        
        // 2. 如果当年分数不存在，使用历史录取数据（2024年及之前）
        Integer historicalScore = getLowestScoreFromAdmissions(admissions);
        if (historicalScore != null) {
            log.debug("【推荐类型判定】使用历史年份分数：专业组={}，分数={}",
                     majorGroupName, historicalScore);
        } else {
            log.warn("【推荐类型判定】无法获取分数：专业组={}，既没有AI录取分数线，也没有历史录取数据",
                     majorGroupName);
        }
        return historicalScore;
    }

    /**
     * 从 cutoffScoreMap 中查找录取分数数据
     * 【优化】使用 schoolUuid 进行精确匹配
     * 
     * @param cutoffScoreMap 预加载的录取分数线数据（key = "schoolUuid_专业组"）
     * @param schoolUuid 学校UUID
     * @param majorGroupName 专业组名称（可能带括号，如"（001）"或"001"）
     * @param year 年份（用于日志）
     * @return 找到的录取分数数据，未找到返回 null
     */
    private AiAdmissionCutoffScoreDO findCutoffScoreFromMap(
            Map<String, AiAdmissionCutoffScoreDO> cutoffScoreMap,
            String schoolUuid,
            String majorGroupName,
            Integer year) {
        
        if (cutoffScoreMap == null || cutoffScoreMap.isEmpty()) {
            log.debug("【查找AI录取分数线】cutoffScoreMap为空");
            return null;
        }
        
        if (StrUtil.isBlank(schoolUuid)) {
            log.debug("【查找AI录取分数线】schoolUuid为空");
            return null;
        }
        
        // 标准化专业组名称
        String[] groupNameVariants = new String[] {
            majorGroupName,  // 原始名称（如：（001））
            majorGroupName.replace("（", "").replace("）", "").replace("(", "").replace(")", ""),  // 去掉括号（如：001）
            majorGroupName.replace("（", "").replace("）", "").replace("(", "").replace(")", "").replaceAll("^[A-Z]+", "")  // 去掉字母前缀（如：L001 -> 001）
        };
        
        // 尝试所有专业组变体
        for (String group : groupNameVariants) {
            String key = schoolUuid + "_" + group;
            AiAdmissionCutoffScoreDO result = cutoffScoreMap.get(key);
            if (result != null) {
                if (log.isDebugEnabled()) {
                    log.debug("【查找AI录取分数线-成功】UUID={}，专业组={}，使用key={}，分数={}", 
                            schoolUuid, majorGroupName, key, result.getLowerScore());
                }
                return result;
            }
        }
        
        // 如果都没找到，输出DEBUG日志
        if (log.isDebugEnabled()) {
            log.debug("【查找AI录取分数线-失败】UUID={}，专业组={}", schoolUuid, majorGroupName);
        }
        
        return null;
    }

    /**
     * 构建历史分数数据
     * 
     * @param cutoffScoreMap 预加载的AI录取分数线数据Map，key="schoolUuid_专业组"
     */
    private void buildHistoryScore(AiVolunteerRecommendItemVO item, 
                                   List<MajorAdmissionDO> admissions,
                                   Map<Integer, Integer> enrollNumsByYear,
                                   AiVolunteerRecommendReqVO reqVO,
                                   String schoolUuid,
                                   String schoolName,
                                   String majorGroupName,
                                   Map<String, AiAdmissionCutoffScoreDO> cutoffScoreMap) {
        JSONArray historyScoreArray = new JSONArray();
        JSONArray parityScoreArray = new JSONArray();

        // 按年份分组
        Map<Integer, List<MajorAdmissionDO>> yearMap = admissions.stream()
                .collect(Collectors.groupingBy(MajorAdmissionDO::getYear));

        // 【调试】只针对重庆电子科技职业大学输出日志
        if (schoolName.contains("重庆电子")) {
            log.info("【调试】构建历史分数 - 学校：{}，专业组：{}，历史录取数据年份：{}，cutoffScoreMap大小：{}", 
                    schoolName, majorGroupName, yearMap.keySet(), cutoffScoreMap.size());
        }
        
        // 【优化】先尝试从 cutoffScoreMap 中查找当年数据（使用schoolUuid精确匹配）
        AiAdmissionCutoffScoreDO currentYearCutoff = findCutoffScoreFromMap(
                cutoffScoreMap, schoolUuid, majorGroupName, reqVO.getYear());

        // 【性能优化】只生成近2年数据（减少处理时间）
        for (int i = 0; i < 2; i++) {
            int year = reqVO.getYear() - i;
            JSONObject historyObj = new JSONObject();
            JSONObject parityObj = new JSONObject();

            // 【优化】如果是当年且找到了AI录取分数线数据，直接使用
            if (i == 0 && currentYearCutoff != null) {
                String enrollNum = "-";
                if (enrollNumsByYear != null && enrollNumsByYear.containsKey(year)) {
                    enrollNum = String.valueOf(enrollNumsByYear.get(year));
                }
                
                String score = currentYearCutoff.getLowerScore();
                String rank = currentYearCutoff.getLowerRank();
                
                // 【调试】只针对重庆电子科技职业大学输出日志
                if (schoolName.contains("重庆电子")) {
                    log.info("【调试】使用AI录取分数线数据 - 年份：{}，分数：{}，位次：{}，招生人数：{}", 
                            year, score, rank, enrollNum);
                }
                
                historyObj.put(String.valueOf(year), score + "," + 
                        (StrUtil.isNotBlank(rank) ? rank : "-") + "," + enrollNum);
                parityObj.put(String.valueOf(year), score);
                
                historyScoreArray.add(historyObj);
                parityScoreArray.add(parityObj);
                continue; // 跳过后续处理，进入下一年
            }

            List<MajorAdmissionDO> yearAdmissions = yearMap.get(year);
            
            // 【调试】只针对重庆电子科技职业大学输出日志
            if (schoolName.contains("重庆电子")) {
                log.info("【调试】处理年份 {} (i={}), yearAdmissions是否为null: {}, 数量: {}", 
                        year, i, yearAdmissions == null, yearAdmissions == null ? 0 : yearAdmissions.size());
            }
            
            if (log.isDebugEnabled()) {
                log.debug("处理年份：{}，录取数据条数：{}，是否有招生计划：{}", 
                        year, 
                        yearAdmissions == null ? 0 : yearAdmissions.size(),
                        enrollNumsByYear != null && enrollNumsByYear.containsKey(year));
            }
            
            if (yearAdmissions != null && !yearAdmissions.isEmpty()) {
                // 找到最低分的专业
                MajorAdmissionDO lowestAdmission = yearAdmissions.stream()
                        .filter(a -> StrUtil.isNotBlank(a.getLowestScore()) && !"-".equals(a.getLowestScore()))
                        .min(Comparator.comparing(a -> {
                            try {
                                return Integer.parseInt(a.getLowestScore());
                            } catch (Exception e) {
                                return Integer.MAX_VALUE;
                            }
                        }))
                        .orElse(null);

                if (lowestAdmission != null) {
                    String lowestScore = lowestAdmission.getLowestScore();
                    String lowestRank = lowestAdmission.getLowestSection();
                    
                    // 从历史招生计划获取招生人数
                    String enrollNum = "-";
                    if (enrollNumsByYear != null && enrollNumsByYear.containsKey(year)) {
                        enrollNum = String.valueOf(enrollNumsByYear.get(year));
                    } else if (log.isDebugEnabled()) {
                        log.debug("年份 {} 未找到招生人数", year);
                    }

                    // historyScore格式: "分数,位次,人数"
                    historyObj.put(String.valueOf(year), lowestScore + "," + 
                            (StrUtil.isNotBlank(lowestRank) ? lowestRank : "-") + "," + enrollNum);

                    // parityScore格式: 只有分数
                    parityObj.put(String.valueOf(year), lowestScore);
                } else {
                    // 有录取数据但无有效分数，检查是否有招生计划
                    String enrollNum = "-";
                    if (enrollNumsByYear != null && enrollNumsByYear.containsKey(year)) {
                        enrollNum = String.valueOf(enrollNumsByYear.get(year));
                    }
                    historyObj.put(String.valueOf(year), "-,-," + enrollNum);
                    parityObj.put(String.valueOf(year), "-");
                }
            } else {
                // 没有录取数据，也没有在前面使用AI录取分数线数据
                String enrollNum = "-";
                if (enrollNumsByYear != null && enrollNumsByYear.containsKey(year)) {
                    enrollNum = String.valueOf(enrollNumsByYear.get(year));
                }
                historyObj.put(String.valueOf(year), "-,-," + enrollNum);
                parityObj.put(String.valueOf(year), "-");
            }

            historyScoreArray.add(historyObj);
            parityScoreArray.add(parityObj);
        }

        item.setHistoryScore(historyScoreArray.toJSONString());
        item.setParityScore(parityScoreArray.toJSONString());
        
        // 【调试】只针对重庆电子科技职业大学输出最终结果
        if (schoolName.contains("重庆电子")) {
            log.info("【调试】最终historyScore - 学校：{}，专业组：{}，结果：{}", 
                    schoolName, majorGroupName, item.getHistoryScore());
        }
    }

    /**
     * 计算录取概率（基于分数差值）
     */
    private Integer calculateAdmissionProbability(List<MajorAdmissionDO> admissions, 
                                                  Integer userRank, 
                                                  Integer userScore) {
        if (userScore == null || admissions.isEmpty()) {
            return 50; // 默认50%
        }

        // 获取最近一年有效分数的数据
        MajorAdmissionDO latestAdmission = admissions.stream()
                .filter(a -> StrUtil.isNotBlank(a.getLowestScore()) && !"-".equals(a.getLowestScore()))
                .max(Comparator.comparing(MajorAdmissionDO::getYear))
                .orElse(null);

        if (latestAdmission == null) {
            return 50;
        }

        try {
            int lowestScore = Integer.parseInt(latestAdmission.getLowestScore());
            
            // 基于分数差值计算概率
            // scoreDiff = 录取分数 - 用户分数
            int scoreDiff = lowestScore - userScore;
            
            // 分数差值越大（录取分数高），概率越低
            if (scoreDiff > 30) {
                // 分数差30分以上：5-15%（很难录取）
                return 5 + (int) (Math.random() * 10);
            } else if (scoreDiff > 15) {
                // 分数差15-30分：15-30%（冲刺）
                return 15 + (int) (Math.random() * 15);
            } else if (scoreDiff > 0) {
                // 分数差0-15分：30-50%（有希望）
                return 30 + (int) (Math.random() * 20);
            } else if (scoreDiff > -10) {
                // 分数差-10-0分：50-70%（稳妥）
                return 50 + (int) (Math.random() * 20);
            } else if (scoreDiff > -20) {
                // 分数差-20--10分：70-85%（较稳）
                return 70 + (int) (Math.random() * 15);
            } else {
                // 分数差-20分以下：85-95%（保底）
                return 85 + (int) (Math.random() * 10);
            }
        } catch (Exception e) {
            log.warn("计算录取概率失败", e);
            return 50;
        }
    }

    /**
     * 基于分数差值确定推荐类型
     * 
     * @param admissionScore 最近年份的录取最低分
     * @param userScore 用户分数
     * @return 推荐类型：CHONG-冲刺，WEN-稳妥，BAO-保底
     */
    private String determineRecommendationTypeByScore(Integer admissionScore, Integer userScore) {
        if (admissionScore == null || userScore == null) {
            return "UNKNOWN";
        }

        // 计算分数差值：录取分数 - 用户分数
        int scoreDiff = admissionScore - userScore;

        // 推荐类型判定规则：
        // 1. 冲刺：录取最低分 > 用户分数（即 scoreDiff > 0）
        // 2. 稳妥：录取最低分 = 用户分数 或 比用户分数少5分以内（即 -5 <= scoreDiff <= 0）
        // 3. 保底：其他情况（即 scoreDiff < -5）
        
        if (scoreDiff > 0) {
            // 录取分数 > 用户分数 → 冲刺
            return "CHONG";
        } else if (scoreDiff >= -5) {
            // -5 <= 分数差 <= 0 → 稳妥（录取分数等于或比用户分数少5分以内）
            return "WEN";
        } else {
            // 分数差 < -5 → 保底（用户分数比录取分数高超过5分）
            return "BAO";
        }
    }

    /**
     * 应用筛选条件（优化版：大部分筛选已在构建阶段完成，这里只保留兜底检查）
     * 注意：学校名称、推荐类型、学校性质、学校类别、城市、标签筛选已在buildRecommendations中完成
     */
    private List<AiVolunteerRecommendItemVO> applyFilters(
            List<AiVolunteerRecommendItemVO> recommendations, 
            AiVolunteerRecommendReqVO reqVO) {
        
        // 【优化10】大部分筛选已在构建阶段完成，这里直接返回
        // 如果后续需要额外的筛选逻辑，可以在这里添加
        return recommendations;
    }

    /**
     * 按指定年份的录取分数排序（从高到低）
     * 【性能优化】预先提取所有分数到缓存，避免排序时重复解析JSON（可减少50-70%的排序时间）
     * 
     * @param recommendations 推荐列表
     * @param targetYear 目标年份（如2025）
     * @return 排序后的列表
     */
    private List<AiVolunteerRecommendItemVO> sortByScore(
            List<AiVolunteerRecommendItemVO> recommendations,
            int targetYear) {
        
        // 【关键优化】预先提取所有分数到Map缓存，避免排序时重复解析JSON
        // 原来：如果有500条数据，排序可能调用3000+次JSON解析
        // 现在：只解析500次，然后直接从Map查找
        Map<AiVolunteerRecommendItemVO, Integer> scoreCache = new HashMap<>(recommendations.size());
        for (AiVolunteerRecommendItemVO item : recommendations) {
            Integer score = extractScoreFromHistoryScore(item.getHistoryScore(), targetYear);
            scoreCache.put(item, score);
        }
        
        // 使用预先提取的分数进行排序
        recommendations.sort((item1, item2) -> {
            Integer score1 = scoreCache.get(item1);
            Integer score2 = scoreCache.get(item2);
            
            // 处理空值：有分数的排在前面，没有分数的排在后面
            if (score1 == null && score2 == null) {
                return 0;
            }
            if (score1 == null) {
                return 1;  // item1没有分数，排在后面
            }
            if (score2 == null) {
                return -1; // item2没有分数，排在后面
            }
            
            // 从高到低排序
            return score2.compareTo(score1);
        });
        
        return recommendations;
    }

    /**
     * 从historyScore中提取指定年份的分数
     * 
     * @param historyScoreJson historyScore的JSON字符串
     * @param targetYear 目标年份
     * @return 分数，如果没有则返回null
     */
    private Integer extractScoreFromHistoryScore(String historyScoreJson, int targetYear) {
        if (StrUtil.isBlank(historyScoreJson)) {
            return null;
        }
        
        try {
            // 解析JSON: "[{\"2025\":\"-,-,37\"},{\"2024\":\"534,82261,35\"}]"
            JSONArray historyArray = JSON.parseArray(historyScoreJson);
            
            for (int i = 0; i < historyArray.size(); i++) {
                JSONObject yearObj = historyArray.getJSONObject(i);
                String yearValue = yearObj.getString(String.valueOf(targetYear));
                
                if (StrUtil.isNotBlank(yearValue)) {
                    // 格式: "分数,位次,人数"，提取分数部分
                    String[] parts = yearValue.split(",");
                    if (parts.length > 0 && !"-".equals(parts[0])) {
                        try {
                            return Integer.parseInt(parts[0]);
                        } catch (NumberFormatException e) {
                            if (log.isDebugEnabled()) {
                                log.debug("解析分数失败：{}", parts[0]);
                            }
                            return null;
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.warn("提取历史分数失败，historyScore: {}", historyScoreJson, e);
        }
        
        return null;
    }

    /**
     * 分页处理
     */
    private AiVolunteerRecommendRespVO paginate(
            List<AiVolunteerRecommendItemVO> recommendations, 
            AiVolunteerRecommendReqVO reqVO) {
        
        AiVolunteerRecommendRespVO respVO = new AiVolunteerRecommendRespVO();
        
        int total = recommendations.size();
        int pageNum = reqVO.getPageNum();
        int pageSize = reqVO.getPageSize();
        int startIndex = (pageNum - 1) * pageSize;
        int endIndex = Math.min(startIndex + pageSize, total);

        List<AiVolunteerRecommendItemVO> pageData = new ArrayList<>();
        if (startIndex < total) {
            pageData = recommendations.subList(startIndex, endIndex);
        }

        respVO.setTotal((long) total);
        respVO.setList(pageData);
        respVO.setPageNum(pageNum);
        respVO.setPageSize(pageSize);
        respVO.setSize(pageData.size());
        respVO.setStartRow(startIndex);
        respVO.setEndRow(endIndex);
        respVO.setPages((total + pageSize - 1) / pageSize);
        respVO.setPrePage(pageNum > 1 ? pageNum - 1 : 0);
        respVO.setNextPage(endIndex < total ? pageNum + 1 : 0);
        respVO.setIsFirstPage(pageNum == 1);
        respVO.setIsLastPage(endIndex >= total);
        respVO.setHasPreviousPage(pageNum > 1);
        respVO.setHasNextPage(endIndex < total);
        respVO.setNavigatePages(8);
        
        // 计算导航页码
        int pages = respVO.getPages();
        List<Integer> navigatePageNums = new ArrayList<>();
        int navigateFirstPage = Math.max(1, pageNum - 4);
        int navigateLastPage = Math.min(pages, pageNum + 3);
        for (int i = navigateFirstPage; i <= navigateLastPage; i++) {
            navigatePageNums.add(i);
        }
        respVO.setNavigatepageNums(navigatePageNums);
        respVO.setNavigateFirstPage(navigateFirstPage);
        respVO.setNavigateLastPage(navigateLastPage);

        return respVO;
    }

    @Override
//    @Cacheable(
//        cacheManager = "aiVolunteerCacheManager",
//        value = "ai:volunteer:count",
//        key = "#reqVO.province + ':' + #reqVO.year + ':' + #reqVO.score + ':' + #reqVO.ranks + ':' + " +
//              "#reqVO.classify + ':' + #reqVO.batch + ':' + " +
//              "(#reqVO.universityName != null ? #reqVO.universityName : 'null') + ':' + " +
//              "(#reqVO.propertyName != null ? #reqVO.propertyName : 'null') + ':' + " +
//              "(#reqVO.categoryName != null ? #reqVO.categoryName : 'null') + ':' + " +
//              "(#reqVO.citys != null ? #reqVO.citys.toString() : 'null') + ':' + " +
//              "(#reqVO.provinces != null ? #reqVO.provinces.toString() : 'null') + ':' + " +
//              "(#reqVO.universityLevel != null ? #reqVO.universityLevel : 'null') + ':' + " +
//              "(#reqVO.universityTag != null ? #reqVO.universityTag : 'null') + ':' + " +
//              "(#reqVO.universityTagNames != null ? #reqVO.universityTagNames.toString() : 'null') + ':' + " +
//              "(#reqVO.major != null ? #reqVO.major : 'null') + ':' + " +
//              "(#reqVO.entrantType != null ? #reqVO.entrantType : 'null') + ':' + " +
//              "(#reqVO.subjects != null ? #reqVO.subjects : 'null') + ':' + " +
//              "(#reqVO.artCategory != null ? #reqVO.artCategory : 'null') + ':' + " +
//              "(#reqVO.artScore != null ? #reqVO.artScore : 'null') + ':' + " +
//              "(#reqVO.artRank != null ? #reqVO.artRank : 'null') + ':' + " +
//              "(#reqVO.minScore != null ? #reqVO.minScore : 'null') + ':' + " +
//              "(#reqVO.maxScore != null ? #reqVO.maxScore : 'null') + ':' + " +
//              "(#reqVO.type != null ? #reqVO.type : 'null')",
//        unless = "#result == null"
//    )
    public AiVolunteerCountRespVO countVolunteers(AiVolunteerRecommendReqVO reqVO) {
        log.info("【极简版-count】开始统计，参数：{}", JSON.toJSONString(reqVO));
        long startTime = System.currentTimeMillis();

        // 1. 直接从 AI录取分数线表查询所有符合条件的记录
        List<AiAdmissionCutoffScoreDO> cutoffScores = aiAdmissionCutoffScoreMapper.selectMajorGroupsWithPagination(
            reqVO.getProvince(),
            reqVO.getYear(),
            reqVO.getClassify(),
            reqVO.getBatch(),
            reqVO.getMinScore(),
            reqVO.getMaxScore(),
            0,  // offset
            Integer.MAX_VALUE  // limit - 查询所有
        );
        
        log.info("【极简版-count】从AI录取分数线表查询到 {} 条记录", cutoffScores.size());

        // 2. 选科要求过滤
        List<String> userSubjects = null;
        if (StrUtil.isNotBlank(reqVO.getSubjects())) {
            userSubjects = Arrays.asList(reqVO.getSubjects().split(","));
            int beforeFilter = cutoffScores.size();
            
            final List<String> finalUserSubjects = userSubjects;
            cutoffScores = cutoffScores.stream()
                .filter(cutoff -> matchSubjectRequirementsForCount(cutoff.getSubjectRequirement(), finalUserSubjects))
                .collect(Collectors.toList());
            
            log.info("【极简版-count】选科过滤：从 {} 条筛选到 {} 条", beforeFilter, cutoffScores.size());
        }

        // 3. 提取所有涉及的学校名称，查询学校信息（用于学校类型统计）
        Set<String> schoolNames = cutoffScores.stream()
            .map(AiAdmissionCutoffScoreDO::getSchoolName)
            .filter(StrUtil::isNotBlank)
            .collect(Collectors.toSet());
        
        Map<String, CollegeDO> collegeMap = new HashMap<>();
        if (!schoolNames.isEmpty()) {
            long collegeQueryStart = System.currentTimeMillis();
            List<CollegeDO> colleges = collegeMapper.selectList(
                new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<CollegeDO>()
                    .in(CollegeDO::getCollegeName, schoolNames)
            );
            
            // 构建学校名称到学校信息的映射
            collegeMap = colleges.stream()
                .collect(Collectors.toMap(CollegeDO::getCollegeName, c -> c, (c1, c2) -> c1));
            
            log.info("【极简版-count】查询学校信息：{} 所学校，耗时：{}ms", 
                colleges.size(), System.currentTimeMillis() - collegeQueryStart);
        }

        // 4. 按类型统计 + 学校类型统计
        int sprintNum = 0;   // 冲刺（CHONG）
        int reliableNum = 0; // 稳妥（WEN）
        int guardNum = 0;    // 保底（BAO）
        
        // 学校类型统计（使用Set去重）
        Set<String> schools985 = new HashSet<>();
        Set<String> schools211 = new HashSet<>();
        Set<String> schoolsDualClass = new HashSet<>();
        Set<String> schoolsMinistryDirect = new HashSet<>();
        Set<String> schoolsPublic = new HashSet<>();
        Set<String> schoolsPrivate = new HashSet<>();
        
        Integer userScore = reqVO.getScore();
        if (userScore == null) {
            userScore = 0;  // 默认值
        }
        
        for (AiAdmissionCutoffScoreDO cutoff : cutoffScores) {
            try {
                // 解析录取分数
                String lowerScoreStr = cutoff.getLowerScore();
                if (lowerScoreStr == null || lowerScoreStr.trim().isEmpty()) {
                continue;
            }

                int lowerScore = Integer.parseInt(lowerScoreStr.trim());
                int scoreDiff = lowerScore - userScore;
                
                // 根据分数差判定类型
                if (scoreDiff > 0) {
                    sprintNum++;  // 冲刺：录取分数 > 用户分数
                } else if (scoreDiff >= -5) {
                    reliableNum++;  // 稳妥：用户分数-5 <= 录取分数 <= 用户分数
                } else {
                    guardNum++;  // 保底：录取分数 < 用户分数-5
                }
                
                // 学校类型统计（去重统计，每所学校只统计一次）
                String schoolName = cutoff.getSchoolName();
                if (StrUtil.isNotBlank(schoolName)) {
                    CollegeDO college = collegeMap.get(schoolName);
                if (college != null) {
                        // 985院校统计
                        if (Boolean.TRUE.equals(college.getIs985())) {
                            schools985.add(schoolName);
                        }
                        
                        // 211院校统计
                        if (Boolean.TRUE.equals(college.getIs211())) {
                            schools211.add(schoolName);
                        }
                        
                        // 双一流院校统计
                        if (Boolean.TRUE.equals(college.getIsDualClass())) {
                            schoolsDualClass.add(schoolName);
                        }
                        
                        // 教育部直属院校统计
                        if (Boolean.TRUE.equals(college.getIsMinistryOfEducationDirectlyAffiliated())) {
                            schoolsMinistryDirect.add(schoolName);
                        }
                        
                        // 公办/民办院校统计
                        if (StrUtil.isNotBlank(college.getCollegeType())) {
                            if (college.getCollegeType().contains("公办") || college.getCollegeType().contains("公立")) {
                                schoolsPublic.add(schoolName);
                            } else if (college.getCollegeType().contains("民办") || college.getCollegeType().contains("私立")) {
                                schoolsPrivate.add(schoolName);
                            }
                        }
                    }
                }
            } catch (NumberFormatException e) {
                log.warn("【极简版-count】分数解析失败：{}, 学校={}, 专业组={}", 
                    cutoff.getLowerScore(), cutoff.getSchoolName(), cutoff.getGroupName());
            }
        }
        
        int totalNum = sprintNum + reliableNum + guardNum;
        
        long totalTime = System.currentTimeMillis() - startTime;
        log.info("【极简版-count】统计完成，总耗时：{}ms，结果：总数={}, 冲刺={}, 稳妥={}, 保底={}, 985={}, 211={}, 双一流={}, 教育部直属={}", 
            totalTime, totalNum, sprintNum, reliableNum, guardNum,
            schools985.size(), schools211.size(), schoolsDualClass.size(), schoolsMinistryDirect.size());
        
        // 【调试】输出保底类型的学校和专业组（用于与 recommend 接口对比）
        if (log.isDebugEnabled() && guardNum > 0) {
            log.debug("【count接口-保底列表】共 {} 个：", guardNum);
            int count = 0;
            for (AiAdmissionCutoffScoreDO cutoff : cutoffScores) {
                try {
                    String lowerScoreStr = cutoff.getLowerScore();
                    if (lowerScoreStr == null || lowerScoreStr.trim().isEmpty()) {
                            continue;
                        }
                    int lowerScore = Integer.parseInt(lowerScoreStr.trim());
                    int scoreDiff = lowerScore - userScore;
                    if (scoreDiff < -5) {
                        count++;
                        if (count <= 10) {  // 只输出前10个
                            log.debug("  {}. 学校={}, 专业组={}, 分数={}, 分数差={}", 
                                count, cutoff.getSchoolName(), cutoff.getGroupName(), lowerScore, scoreDiff);
                        }
                    }
                } catch (NumberFormatException e) {
                    // ignore
                }
            }
        }

        // 5. 返回结果
        return new AiVolunteerCountRespVO()
            .setAllNum(totalNum)
            .setSprintNum(sprintNum)
            .setReliableNum(reliableNum)
            .setGuardNum(guardNum)
            .setCount985(schools985.size())
            .setCount211(schools211.size())
            .setCountDualClass(schoolsDualClass.size())
            .setCountMinistryDirect(schoolsMinistryDirect.size())
            .setCountPublic(schoolsPublic.size())
            .setCountPrivate(schoolsPrivate.size());
    }

    /**
     * 选科要求匹配（专用于count接口，处理AI录取分数线表的格式）
     * 
     * AI录取分数线表中的选科要求格式：
     * - 不限
     * - 单科目：思想政治、地理、化学、生物学等
     * - 组合：化学+地理、化学+思想政治等
     */
    private boolean matchSubjectRequirementsForCount(String requirements, List<String> userSubjects) {
        if (StrUtil.isBlank(requirements)) {
            return true;
        }

        // 不限
        if (requirements.contains("不限") || requirements.contains("无要求")) {
            return true;
        }

        // 标准化用户选科（转换为Set便于查找）
        Set<String> userSubjectSet = new HashSet<>(userSubjects);
        
        // 科目名称标准化映射（处理可能的同义词）
        Map<String, Set<String>> subjectAliases = new HashMap<>();
        subjectAliases.put("生物", new HashSet<>(Arrays.asList("生物", "生物学")));
        subjectAliases.put("生物学", new HashSet<>(Arrays.asList("生物", "生物学")));
        subjectAliases.put("政治", new HashSet<>(Arrays.asList("政治", "思想政治")));
        subjectAliases.put("思想政治", new HashSet<>(Arrays.asList("政治", "思想政治")));
        
        // 扩展用户选科（包含同义词）
        Set<String> expandedUserSubjects = new HashSet<>(userSubjectSet);
        for (String subject : userSubjectSet) {
            if (subjectAliases.containsKey(subject)) {
                expandedUserSubjects.addAll(subjectAliases.get(subject));
            }
        }
        
        // 处理 "+" 分隔的组合（如：化学+地理）
        if (requirements.contains("+")) {
            String[] requiredSubjects = requirements.split("\\+");
            // 用户必须选了所有要求的科目
            for (String required : requiredSubjects) {
                String normalizedRequired = required.trim();
                boolean matched = false;
                
                // 检查用户是否选了这个科目（包含同义词）
                if (expandedUserSubjects.contains(normalizedRequired)) {
                    matched = true;
                } else {
                    // 检查同义词
                    for (String userSubject : expandedUserSubjects) {
                        if (subjectAliases.containsKey(normalizedRequired)) {
                            if (subjectAliases.get(normalizedRequired).contains(userSubject)) {
                                matched = true;
                                break;
                            }
                        }
                    }
                }
                
                if (!matched) {
                    return false;  // 有任何一个科目不满足，则过滤掉
                }
            }
            return true;
        }
        
        // 单科目要求：用户只需要选了这个科目即可
        String normalizedRequirement = requirements.trim();
        
        // 直接匹配
        if (expandedUserSubjects.contains(normalizedRequirement)) {
            return true;
        }
        
        // 检查同义词匹配
        for (String userSubject : expandedUserSubjects) {
            if (subjectAliases.containsKey(normalizedRequirement)) {
                if (subjectAliases.get(normalizedRequirement).contains(userSubject)) {
                    return true;
                }
            }
        }
        
        return false;
    }


    @Override
    public AiMajorRecommendRespVO recommendMajors(AiMajorRecommendReqVO reqVO) {
        log.info("开始AI智能推荐专业列表，参数：{}", JSON.toJSONString(reqVO));

        long startTime = System.currentTimeMillis();

        // 1. 根据学校UUID和专业组查询招生计划中的专业列表
        // 注意：前端传递的 recruitCode 实际是 schoolUuid
        String schoolUuid = reqVO.getRecruitCode();
        String groupIdentifier = reqVO.getUniversityMajorGroup();
        List<CollegeEnrollmentPlanDO> majorPlans = queryMajorsByGroup(schoolUuid, groupIdentifier, reqVO);
        log.info("步骤1-查询专业列表：{} 条", majorPlans.size());

        if (majorPlans.isEmpty()) {
            log.warn("未找到匹配的专业，学校UUID={}, 专业组={}", schoolUuid, groupIdentifier);
            AiMajorRecommendRespVO respVO = new AiMajorRecommendRespVO();
            respVO.setIntentionList(new ArrayList<>());
            respVO.setNotIntentionList(new ArrayList<>());
            return respVO;
        }

        // 2. 查询学校信息
        CollegeDO college = collegeMapper.selectOne(
                new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<CollegeDO>()
                        .eq(CollegeDO::getSchoolUuid, schoolUuid)
        );
        log.info("步骤2-查询学校信息完成");

        // 3. 查询历史录取数据（用于构建历史分数）
        List<Integer> historyYears = Arrays.asList(reqVO.getYear(), reqVO.getYear() - 1, reqVO.getYear() - 2);
        List<MajorAdmissionDO> historyAdmissions = majorAdmissionMapper.selectList(
                new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<MajorAdmissionDO>()
                        .eq(MajorAdmissionDO::getSchoolUuid, schoolUuid)
                        .eq(MajorAdmissionDO::getProvinceName, reqVO.getProvince())
                        .in(MajorAdmissionDO::getYear, historyYears)
                        .orderByDesc(MajorAdmissionDO::getYear)
        );
        log.info("步骤3-查询历史录取数据：{} 条", historyAdmissions.size());

        // 4. 查询历史招生人数
        Map<String, Map<Integer, Integer>> historyEnrollmentNums = queryHistoryEnrollmentNumbersForMajors(
                schoolUuid, groupIdentifier, reqVO);
        log.info("步骤4-查询历史招生人数完成");

        // 5. 构建专业列表
        List<AiMajorItemVO> majorList = buildMajorList(majorPlans, historyAdmissions, 
                historyEnrollmentNums, college, reqVO);
        log.info("步骤5-构建专业列表：{} 条", majorList.size());

        // 6. 分类为意向和非意向（目前全部作为意向专业）
        AiMajorRecommendRespVO respVO = new AiMajorRecommendRespVO();
        respVO.setIntentionList(majorList);
        respVO.setNotIntentionList(new ArrayList<>());

        long totalTime = System.currentTimeMillis() - startTime;
        log.info("AI智能推荐专业列表完成，总耗时：{}ms，专业数量：{}", totalTime, majorList.size());

        return respVO;
    }

    /**
     * 根据学校UUID和专业组查询专业列表
     */
    private List<CollegeEnrollmentPlanDO> queryMajorsByGroup(String schoolUuid, String groupIdentifier, 
                                                             AiMajorRecommendReqVO reqVO) {
        // 1. 先查询该学校的所有招生计划（不加专业组条件）
        List<CollegeEnrollmentPlanDO> allPlans = enrollmentPlanMapper.selectList(
                new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<CollegeEnrollmentPlanDO>()
                        .eq(CollegeEnrollmentPlanDO::getSchoolUuid, schoolUuid)
                        .eq(CollegeEnrollmentPlanDO::getProvinceName, reqVO.getProvince())
                        .eq(CollegeEnrollmentPlanDO::getYear, reqVO.getYear())
                        .eq(CollegeEnrollmentPlanDO::getType, reqVO.getClassify())
                        .eqIfPresent(CollegeEnrollmentPlanDO::getBatchName, reqVO.getBatch())
        );
        
        // 2. 如果没有指定专业组，返回所有结果
        if (StrUtil.isBlank(groupIdentifier)) {
            return allPlans;
        }
        
        // 3. 在内存中通过专业组标识符过滤（支持跨年度匹配）
        return allPlans.stream()
                .filter(plan -> {
                    String planGroupIdentifier = extractGroupIdentifier(plan.getSpecialGroupName());
                    return groupIdentifier.equals(planGroupIdentifier);
                })
                .collect(Collectors.toList());
    }

    /**
     * 查询专业维度的历史招生人数
     * 简化版：只按专业名称、学校UUID、年份、省份查询，不使用专业组过滤
     */
    private Map<String, Map<Integer, Integer>> queryHistoryEnrollmentNumbersForMajors(
            String schoolUuid, String groupIdentifier, AiMajorRecommendReqVO reqVO) {
        
        List<Integer> historyYears = Arrays.asList(reqVO.getYear() - 1, reqVO.getYear() - 2);
        
        // 查询该学校的所有历史招生计划（按专业名称、学校UUID、年份、省份）
        List<CollegeEnrollmentPlanDO> historyPlans = enrollmentPlanMapper.selectList(
                new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<CollegeEnrollmentPlanDO>()
                        .eq(CollegeEnrollmentPlanDO::getSchoolUuid, schoolUuid)
                        .eq(CollegeEnrollmentPlanDO::getProvinceName, reqVO.getProvince())
                        .eq(CollegeEnrollmentPlanDO::getType, reqVO.getClassify())
                        .in(CollegeEnrollmentPlanDO::getYear, historyYears)
                        .eqIfPresent(CollegeEnrollmentPlanDO::getBatchName, reqVO.getBatch())
        );

        Map<String, Map<Integer, Integer>> result = new HashMap<>();
        
        for (CollegeEnrollmentPlanDO plan : historyPlans) {
            String majorName = plan.getCollegeMajorName();
            if (StrUtil.isBlank(majorName)) {
                continue;
            }
            
            String fullMajorKey = buildFullMajorKey(majorName, plan.getCollegeMajorBracketContent());
            result.putIfAbsent(fullMajorKey, new HashMap<>());
            Map<Integer, Integer> yearMap = result.get(fullMajorKey);
            
            try {
                int enrollNum = Integer.parseInt(plan.getEnrollmentNumbers());
                yearMap.put(plan.getYear(), yearMap.getOrDefault(plan.getYear(), 0) + enrollNum);
            } catch (Exception e) {
                log.warn("解析招生人数失败，专业：{}，年份：{}，招生人数：{}", 
                        fullMajorKey, plan.getYear(), plan.getEnrollmentNumbers());
            }
        }
        
        log.info("历史招生人数聚合完成，匹配到 {} 条记录，聚合后专业数量：{}", 
                historyPlans.size(), result.size());
        
        return result;
    }

    /**
     * 构建专业列表
     */
    private List<AiMajorItemVO> buildMajorList(
            List<CollegeEnrollmentPlanDO> majorPlans,
            List<MajorAdmissionDO> historyAdmissions,
            Map<String, Map<Integer, Integer>> historyEnrollmentNums,
            CollegeDO college,
            AiMajorRecommendReqVO reqVO) {

        List<AiMajorItemVO> majorList = new ArrayList<>();

        // 按年份分组历史录取数据
        Map<Integer, List<MajorAdmissionDO>> yearAdmissionMap = historyAdmissions.stream()
                .collect(Collectors.groupingBy(MajorAdmissionDO::getYear));

        for (CollegeEnrollmentPlanDO plan : majorPlans) {
            String majorName = plan.getCollegeMajorName();
            if (StrUtil.isNotBlank(majorName)) {
                String fullMajorKey = buildFullMajorKey(majorName, plan.getCollegeMajorBracketContent());
                historyEnrollmentNums.putIfAbsent(fullMajorKey, new HashMap<>());
                Map<Integer, Integer> yearMap = historyEnrollmentNums.get(fullMajorKey);
                
                try {
                    int enrollNum = Integer.parseInt(plan.getEnrollmentNumbers());
                    yearMap.put(reqVO.getYear(), yearMap.getOrDefault(reqVO.getYear(), 0) + enrollNum);
                } catch (Exception e) {
                    log.warn("解析当年招生人数失败，专业：{}，招生人数：{}", fullMajorKey, plan.getEnrollmentNumbers());
                }
            }
        }
        
        log.info("添加当年招生人数后，历史招生人数Map包含 {} 个专业", historyEnrollmentNums.size());

        for (CollegeEnrollmentPlanDO plan : majorPlans) {
            AiMajorItemVO item = new AiMajorItemVO();

            // 基本信息
            item.setMajorName(plan.getCollegeMajorName());
            item.setMajorCode(plan.getCollegeMajorCode());
            item.setRecruitCode(reqVO.getRecruitCode());
            item.setUniversityMajorGroup(reqVO.getUniversityMajorGroup());
            item.setMajorRemarks(plan.getCollegeMajorBracketContent());
            item.setYear(reqVO.getYear());
            item.setEnrollYear(reqVO.getYear());
            item.setClaim(plan.getCourseSelectionRequirements());
            item.setStudyCost(plan.getStudyCost());
            item.setStudyYear(plan.getInSchoolYears());
            item.setBatch(plan.getBatchName());
            item.setClassify(plan.getType());
            item.setVolunteerType("ACADEMY_GROUP");
            item.setTag("普通类");
            item.setNextYear(reqVO.getYear() + 1);
            item.setFilterMajor(false);
            item.setCheckClaim(false);
            item.setSpecialPlan(false);
            item.setZsgkId(Math.toIntExact(plan.getId()));

            // 招生人数
            try {
                int planNum = Integer.parseInt(plan.getEnrollmentNumbers());
                item.setPlanNum(planNum);
                item.setEnrollNum(planNum);
            } catch (Exception e) {
                item.setPlanNum(0);
                item.setEnrollNum(0);
            }

            // 学费（整数）
            try {
                item.setStudyCostInt(Integer.parseInt(plan.getStudyCost()));
            } catch (Exception e) {
                item.setStudyCostInt(0);
            }

            // 学校信息
            if (college != null) {
                item.setUniversityName(college.getCollegeName());
                item.setUniversityId(college.getSchoolUuid());
                item.setProvince(college.getProvince());
                item.setPropertyName(college.getCollegeType());
                item.setCategoryName(college.getCollegeCategory());
                item.setLogo(college.getCoverImage());
                item.setLevel("本科");
                
                if (CollUtil.isNotEmpty(college.getCollegeTags())) {
                    item.setTags(String.join(",", college.getCollegeTags()));
                }
            }

            // 构建历史分数和同位分
            buildMajorHistoryScore(item, plan.getCollegeMajorName(), yearAdmissionMap, 
                    historyEnrollmentNums, reqVO);

            majorList.add(item);
        }

        return majorList;
    }

    /**
     * 构建专业的历史分数数据
     */
    private void buildMajorHistoryScore(
            AiMajorItemVO item,
            String majorName,
            Map<Integer, List<MajorAdmissionDO>> yearAdmissionMap,
            Map<String, Map<Integer, Integer>> historyEnrollmentNums,
            AiMajorRecommendReqVO reqVO) {

        JSONArray historyScoreArray = new JSONArray();
        JSONArray parityScoreArray = new JSONArray();

        // 调试：打印可用的专业名称
        if (log.isDebugEnabled()) {
            log.debug("当前专业：{}，历史招生人数Map中的专业：{}", majorName, historyEnrollmentNums.keySet());
        }

        // 生成3年数据
        for (int i = 0; i < 3; i++) {
            int year = reqVO.getYear() - i;
            JSONObject historyObj = new JSONObject();
            JSONObject parityObj = new JSONObject();

            List<MajorAdmissionDO> yearAdmissions = yearAdmissionMap.get(year);
            
            // 查找该专业的录取数据
            MajorAdmissionDO majorAdmission = null;
            if (yearAdmissions != null) {
                String baseMajorName = majorName.split("（")[0].trim();
                String targetBracket = StrUtil.trim(item.getMajorRemarks());
                
                
                // 1. 优先完全匹配
                if (StrUtil.isNotBlank(targetBracket)) {
                    majorAdmission = yearAdmissions.stream()
                            .filter(a -> {
                                String admissionMajorName = a.getMajorName();
                                if (StrUtil.isBlank(admissionMajorName)) {
                                    return false;
                                }
                                String baseAdmissionName = admissionMajorName.split("（")[0].trim();
                                String bracketContent = StrUtil.trim(a.getMajorNameBracketContent());
                                return baseAdmissionName.equals(baseMajorName) && StrUtil.equals(bracketContent, targetBracket);
                            })
                            .findFirst()
                            .orElse(null);
                }
                if (majorAdmission == null) {
                    majorAdmission = yearAdmissions.stream()
                            .filter(a -> {
                                String admissionMajorName = a.getMajorName();
                                if (StrUtil.isBlank(admissionMajorName)) {
                                    return false;
                                }
                                // 去除括号后比较
                                String baseAdmissionName = admissionMajorName.split("（")[0].trim();
                                return baseAdmissionName.equals(baseMajorName);
                            })
                            .findFirst()
                            .orElse(null);
                }
                
                // 2. 如果没有完全匹配，尝试精确的开头匹配（避免误匹配）
                if (majorAdmission == null) {
                    majorAdmission = yearAdmissions.stream()
                            .filter(a -> {
                                String admissionMajorName = a.getMajorName();
                                if (StrUtil.isBlank(admissionMajorName)) {
                                    return false;
                                }
                                String baseAdmissionName = admissionMajorName.split("（")[0].trim();
                                // 只有当目标专业是录取专业的开头时才匹配
                                // 例如："计算机"可以匹配"计算机科学与技术"
                                // 但"药学"不会匹配"中药学类"（因为"药学"不是"中药学类"的开头）
                                return baseAdmissionName.startsWith(baseMajorName);
                            })
                            .findFirst()
                            .orElse(null);
                }
                
                // 3. 如果还没找到，尝试反向匹配（录取专业名是目标专业的一部分）
                // 例如："计算机科学与技术"可以匹配到"计算机"的录取数据
                if (majorAdmission == null) {
                    majorAdmission = yearAdmissions.stream()
                            .filter(a -> {
                                String admissionMajorName = a.getMajorName();
                                if (StrUtil.isBlank(admissionMajorName)) {
                                    return false;
                                }
                                String baseAdmissionName = admissionMajorName.split("（")[0].trim();
                                // 录取专业名必须是目标专业的开头部分
                                return baseMajorName.startsWith(baseAdmissionName) && baseAdmissionName.length() >= 2;
                            })
                            .findFirst()
                            .orElse(null);
                }
            }

            if (majorAdmission != null && StrUtil.isNotBlank(majorAdmission.getLowestScore()) 
                    && !"-".equals(majorAdmission.getLowestScore())) {
                String lowestScore = majorAdmission.getLowestScore();
                String lowestRank = majorAdmission.getLowestSection();
                
                String enrollNum = "-";
                String fullMajorKey = buildFullMajorKey(majorName, item.getMajorRemarks());
                Map<Integer, Integer> enrollNumsByYear = historyEnrollmentNums.get(fullMajorKey);
                if (enrollNumsByYear != null && enrollNumsByYear.containsKey(year)) {
                    enrollNum = String.valueOf(enrollNumsByYear.get(year));
                    log.debug("专业：{}，年份：{}，找到招生人数：{}", fullMajorKey, year, enrollNum);
                } else {
                    log.debug("专业：{}，年份：{}，未找到招生人数", fullMajorKey, year);
                }

                historyObj.put(String.valueOf(year), lowestScore + "," + 
                        (StrUtil.isNotBlank(lowestRank) ? lowestRank : "-") + "," + enrollNum);

                try {
                    parityObj.put(String.valueOf(year), Integer.parseInt(lowestScore));
                } catch (Exception e) {
                    parityObj.put(String.valueOf(year), "-");
                }
            } else {
                String enrollNum = "-";
                String fullMajorKey = buildFullMajorKey(majorName, item.getMajorRemarks());
                Map<Integer, Integer> enrollNumsByYear = historyEnrollmentNums.get(fullMajorKey);
                if (enrollNumsByYear != null && enrollNumsByYear.containsKey(year)) {
                    enrollNum = String.valueOf(enrollNumsByYear.get(year));
                    log.debug("专业：{}，年份：{}，找到招生人数（无录取数据）：{}", fullMajorKey, year, enrollNum);
                }
                historyObj.put(String.valueOf(year), "-,-," + enrollNum);
                parityObj.put(String.valueOf(year), "-");
            }

            historyScoreArray.add(historyObj);
            parityScoreArray.add(parityObj);
        }

        item.setHistoryScore(historyScoreArray.toJSONString());
        item.setParityScore(parityScoreArray.toJSONString());
    }

    private String buildFullMajorKey(String baseMajorName, String bracketContent) {
        if (StrUtil.isBlank(baseMajorName)) {
            return baseMajorName;
        }
        String trimmedBracket = StrUtil.trim(bracketContent);
        if (StrUtil.isBlank(trimmedBracket)) {
            return baseMajorName.trim();
        }
        return baseMajorName.trim() + "（" + trimmedBracket + "）";
    }

    private String extractBracketContent(String majorName) {
        if (StrUtil.isBlank(majorName)) {
            return null;
        }
        int left = majorName.indexOf('（');
        int right = majorName.indexOf('）');
        if (left >= 0 && right > left) {
            return majorName.substring(left + 1, right).trim();
        }
        return null;
    }
}
